define("gallery/mathjs/0.9.0/math-debug", [], function(require, exports, module) {
    /**
 * math.js
 * https://github.com/josdejong/mathjs
 *
 * Math.js is an extensive math library for JavaScript and Node.js,
 * It features real and complex numbers, units, matrices, a large set of
 * mathematical functions, and a flexible expression parser.
 *
 * @version 0.9.0
 * @date    2013-05-29
 *
 * @license
 * Copyright (C) 2013 Jos de Jong <wjosdejong@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
    (function() {
        // TODO: put "use strict"; here (but right now webstorms inspector starts
        // complaining on this issue: http://youtrack.jetbrains.com/issue/WEB-7485)
        /**
 * Define namespace
 */
        var math = {
            type: {},
            expr: {
                node: {}
            },
            options: {
                precision: 5
            }
        };
        /**
 * CommonJS module exports
 */
        if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
            module.exports = math;
        }
        if (typeof exports !== "undefined") {
            exports = math;
        }
        /**
 * AMD module exports
 */
        if (typeof require != "undefined" && typeof define != "undefined") {
            define(function() {
                return math;
            });
        }
        /**
 * Browser exports
 */
        if (typeof window != "undefined") {
            window["math"] = math;
        }
        // utility methods for strings, objects, and arrays
        var util = function() {
            var util = {};
            /**
     * Convert a number to a formatted string representation.
     * @param {Number} value            The value to be formatted
     * @param {Number} [digits]         number of digits
     * @return {String} formattedValue  The formatted value
     */
            util.formatNumber = function formatNumber(value, digits) {
                if (value === Infinity) {
                    return "Infinity";
                } else if (value === -Infinity) {
                    return "-Infinity";
                } else if (value === NaN) {
                    return "NaN";
                }
                // TODO: what is a nice limit for non-scientific values?
                var abs = Math.abs(value);
                if (abs > .001 && abs < 1e5 || abs == 0) {
                    // round the value to a limited number of digits
                    return util.toPrecision(value, digits);
                } else {
                    // scientific notation
                    var exp = Math.round(Math.log(abs) / Math.LN10);
                    var v = value / Math.pow(10, exp);
                    return util.toPrecision(v, digits) + "e" + exp;
                }
            };
            /**
     * Round a value to a maximum number of digits. Trailing zeros will be
     * removed.
     * @param {Number} value
     * @param {Number} [digits]
     * @returns {string} str
     */
            util.toPrecision = function(value, digits) {
                return value.toPrecision(digits).replace(_trailingZeros, function(a, b, c) {
                    return a.substring(0, a.length - (b.length ? 0 : 1) - c.length);
                });
            };
            /** @private */
            var _trailingZeros = /\.(\d*?)(0+)$/g;
            /**
     * Recursively format an n-dimensional matrix
     * Example output: "[[1, 2], [3, 4]]"
     * @param {Array} array
     * @returns {String} str
     */
            util.formatArray = function formatArray(array) {
                if (array instanceof Array) {
                    var str = "[";
                    var len = array.length;
                    for (var i = 0; i < len; i++) {
                        if (i != 0) {
                            str += ", ";
                        }
                        str += util.formatArray(array[i]);
                    }
                    str += "]";
                    return str;
                } else {
                    return math.format(array);
                }
            };
            /**
     * Recursively format an n-dimensional array, output looks like
     * "[1, 2, 3]"
     * @param {Array} array
     * @returns {string} str
     */
            util.formatArray2d = function formatArray2d(array) {
                var str = "[";
                var s = util.size(array);
                if (s.length != 2) {
                    throw new RangeError("Array must be two dimensional (size: " + util.formatArray(s) + ")");
                }
                var rows = s[0];
                var cols = s[1];
                for (var r = 0; r < rows; r++) {
                    if (r != 0) {
                        str += "; ";
                    }
                    var row = array[r];
                    for (var c = 0; c < cols; c++) {
                        if (c != 0) {
                            str += ", ";
                        }
                        var cell = row[c];
                        if (cell != undefined) {
                            str += math.format(cell);
                        }
                    }
                }
                str += "]";
                return str;
            };
            /**
     * Convert function arguments to an array. Arguments can have the following
     * signature:
     *     fn()
     *     fn(n)
     *     fn(m, n, p, ...)
     *     fn([m, n, p, ...])
     * @param {...Number | Array | Matrix} args
     * @returns {Array} array
     */
            util.argsToArray = function argsToArray(args) {
                var array;
                if (args.length == 0) {
                    // fn()
                    array = [];
                } else if (args.length == 1) {
                    // fn(n)
                    // fn([m, n, p, ...])
                    array = args[0];
                    if (array instanceof Matrix) {
                        array = array.toVector();
                    }
                    if (array instanceof Range) {
                        array = array.valueOf();
                    }
                    if (!(array instanceof Array)) {
                        array = [ array ];
                    }
                } else {
                    // fn(m, n, p, ...)
                    array = [];
                    for (var i = 0; i < args.length; i++) {
                        array[i] = args[i];
                    }
                }
                return array;
            };
            /**
     * Check if a text ends with a certain string.
     * @param {String} text
     * @param {String} search
     */
            util.endsWith = function(text, search) {
                var start = text.length - search.length;
                var end = text.length;
                return text.substring(start, end) === search;
            };
            /**
     * Extend object a with the properties of object b
     * @param {Object} a
     * @param {Object} b
     * @return {Object} a
     */
            util.extend = function(a, b) {
                for (var prop in b) {
                    if (b.hasOwnProperty(prop)) {
                        a[prop] = b[prop];
                    }
                }
                return a;
            };
            /**
     * Create a semi UUID
     * source: http://stackoverflow.com/a/105074/1262753
     * @return {String} uuid
     */
            util.randomUUID = function randomUUID() {
                var S4 = function() {
                    return Math.floor(Math.random() * 65536).toString(16);
                };
                return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
            };
            /**
     * Execute function fn element wise for each element in array.
     * Returns an array with the results
     * @param {Array | Matrix | Range} array
     * @param {function} fn
     * @return {Array | Matrix} res
     */
            util.map = function map(array, fn) {
                if (array instanceof Array || array instanceof Matrix || array instanceof Range) {
                    return array.map(function(x) {
                        return fn(x);
                    });
                } else {
                    throw new TypeError("Array expected");
                }
            };
            /**
     * Execute function fn element wise for each entry in two given arrays, or
     * for a (scalar) object and array pair. Returns an array with the results
     * @param {Array | Matrix | Range | Object} array1
     * @param {Array | Matrix | Range | Object} array2
     * @param {function} fn
     * @return {Array | Matrix} res
     */
            util.map2 = function map2(array1, array2, fn) {
                var res, len, i;
                // handle Matrix
                if (array1 instanceof Matrix || array2 instanceof Matrix) {
                    return new Matrix(util.map2(array1.valueOf(), array2.valueOf(), fn));
                }
                // handle Range
                if (array1 instanceof Range || array2 instanceof Range) {
                    // TODO: util.map2 does not utilize Range.map
                    return util.map2(array1.valueOf(), array2.valueOf(), fn);
                }
                if (array1 instanceof Array) {
                    if (array2 instanceof Array) {
                        // fn(array, array)
                        if (array1.length != array2.length) {
                            throw new RangeError("Dimension mismatch " + "(" + array1.length + " != " + array2.length + ")");
                        }
                        res = [];
                        len = array1.length;
                        for (i = 0; i < len; i++) {
                            res[i] = fn(array1[i], array2[i]);
                        }
                    } else {
                        // fn(array, object)
                        res = [];
                        len = array1.length;
                        for (i = 0; i < len; i++) {
                            res[i] = fn(array1[i], array2);
                        }
                    }
                } else {
                    if (array2 instanceof Array) {
                        // fn(object, array)
                        res = [];
                        len = array2.length;
                        for (i = 0; i < len; i++) {
                            res[i] = fn(array1, array2[i]);
                        }
                    } else {
                        // fn(object, object)
                        res = fn(array1, array2);
                    }
                }
                return res;
            };
            /**
     * For each method for objects and arrays.
     * In case of an object, the method loops over all properties of the object.
     * In case of an array, the method loops over all indexes of the array.
     * @param {Object | Array} object   The object
     * @param {function} callback       Callback method, called for each item in
     *                                  the object or array with three parameters:
     *                                  callback(value, index, object)
     */
            util.forEach = function forEach(object, callback) {
                if (object instanceof Array) {
                    object.forEach(callback);
                } else {
                    for (var key in object) {
                        if (object.hasOwnProperty(key)) {
                            callback(object[key], key, object);
                        }
                    }
                }
            };
            /**
     * Creates a new object with the results of calling a provided function on
     * every property in the object.
     * @param {Object} object           The object.
     * @param {function} callback       Mapping function
     * @return {Object | Array} mappedObject
     */
            util.mapObject = function mapObject(object, callback) {
                var m = {};
                for (var key in object) {
                    if (object.hasOwnProperty(key)) {
                        m[key] = callback(object[key]);
                    }
                }
                return m;
            };
            /**
     * Deep test equality of all fields in two pairs of arrays or objects.
     * @param {Array | Object} a
     * @param {Array | Object} b
     * @returns {boolean}
     */
            util.deepEqual = function(a, b) {
                var prop, i, len;
                if (a instanceof Array) {
                    if (!(b instanceof Array)) {
                        return false;
                    }
                    for (i = 0, len = a.length; i < len; i++) {
                        if (!util.deepEqual(a[i], b[i])) {
                            return false;
                        }
                    }
                    return true;
                } else if (a instanceof Object) {
                    if (b instanceof Array || !(b instanceof Object)) {
                        return false;
                    }
                    for (prop in a) {
                        if (a.hasOwnProperty(prop)) {
                            if (!util.deepEqual(a[prop], b[prop])) {
                                return false;
                            }
                        }
                    }
                    for (prop in b) {
                        if (b.hasOwnProperty(prop)) {
                            if (!util.deepEqual(a[prop], b[prop])) {
                                return false;
                            }
                        }
                    }
                    return true;
                } else {
                    return a.valueOf() == b.valueOf();
                }
            };
            /**
     * Recursively calculate the size of a multi dimensional array.
     * @param {Array} x
     * @Return {Number[]} size
     * @throws RangeError
     */
            function _size(x) {
                if (x instanceof Array) {
                    var sizeX = x.length;
                    if (sizeX) {
                        var size0 = _size(x[0]);
                        if (size0[0] == 0) {
                            return [ 0 ].concat(size0);
                        } else {
                            return [ sizeX ].concat(size0);
                        }
                    } else {
                        return [ sizeX ];
                    }
                } else {
                    return [];
                }
            }
            /**
     * Calculate the size of a multi dimensional array.
     * All elements in the array are checked for matching dimensions using the
     * method validate
     * @param {Array} x
     * @Return {Number[]} size
     * @throws RangeError
     */
            util.size = function size(x) {
                // calculate the size
                var s = _size(x);
                // verify the size
                util.validate(x, s);
                return s;
            };
            /**
     * Recursively validate whether each element in a multi dimensional array
     * has a size corresponding to the provided size array.
     * @param {Array} array    Array to be validated
     * @param {Number[]} size  Array with the size of each dimension
     * @param {Number} dim   Current dimension
     * @throws RangeError
     */
            function _validate(array, size, dim) {
                var i;
                var len = array.length;
                if (len != size[dim]) {
                    throw new RangeError("Dimension mismatch (" + len + " != " + size[dim] + ")");
                }
                if (dim < size.length - 1) {
                    // recursively validate each child array
                    var dimNext = dim + 1;
                    for (i = 0; i < len; i++) {
                        var child = array[i];
                        if (!(child instanceof Array)) {
                            throw new RangeError("Dimension mismatch " + "(" + (size.length - 1) + " < " + size.length + ")");
                        }
                        _validate(array[i], size, dimNext);
                    }
                } else {
                    // last dimension. none of the childs may be an array
                    for (i = 0; i < len; i++) {
                        if (array[i] instanceof Array) {
                            throw new RangeError("Dimension mismatch " + "(" + (size.length + 1) + " > " + size.length + ")");
                        }
                    }
                }
            }
            /**
     * Recursively validate whether each array in a multi dimensional array
     * is empty (zero size) and has the correct number dimensions.
     * @param {Array} array    Array to be validated
     * @param {Number[]} size  Array with the size of each dimension
     * @param {Number} dim   Current dimension
     * @throws RangeError
     */
            function _validateEmpty(array, size, dim) {
                if (dim < size.length - 1) {
                    var child = array[0];
                    if (array.length != 1 || !(child instanceof Array)) {
                        throw new RangeError("Dimension mismatch " + "(" + array.length + " > 0)");
                    }
                    _validateEmpty(child, size, dim + 1);
                } else {
                    // last dimension. test if empty
                    if (array.length) {
                        throw new RangeError("Dimension mismatch " + "(" + array.length + " > 0)");
                    }
                }
            }
            /**
     * Validate whether each element in a multi dimensional array has
     * a size corresponding to the provided size array.
     * @param {Array} array    Array to be validated
     * @param {Number[]} size  Array with the size of each dimension
     * @throws RangeError
     */
            util.validate = function validate(array, size) {
                var isScalar = size.length == 0;
                if (isScalar) {
                    // scalar
                    if (array instanceof Array) {
                        throw new RangeError("Dimension mismatch (" + array.length + " != 0)");
                    }
                    return;
                }
                var hasZeros = size.indexOf(0) != -1;
                if (hasZeros) {
                    // array where all dimensions are zero
                    size.forEach(function(value) {
                        if (value != 0) {
                            throw new RangeError("Invalid size, all dimensions must be " + "either zero or non-zero (size: " + util.formatArray(size) + ")");
                        }
                    });
                    _validateEmpty(array, size, 0);
                } else {
                    _validate(array, size, 0);
                }
            };
            /**
     * Test whether index is an integer number with index >= 1 and index <= max
     * @param {*} index       One-based index
     * @param {Number} [max]  One-based maximum value
     */
            util.validateIndex = function(index, max) {
                if (!isNumber(index) || !isInteger(index)) {
                    throw new TypeError("Index must be an integer (value: " + index + ")");
                }
                if (index < 1) {
                    throw new RangeError("Index out of range (" + index + " < 1)");
                }
                if (max && index > max) {
                    throw new RangeError("Index out of range (" + index + " > " + max + ")");
                }
            };
            /**
     * Recursively resize a multi dimensional array
     * @param {Array} array         Array to be resized
     * @param {Number[]} size       Array with the size of each dimension
     * @param {Number} dim          Current dimension
     * @param {*} [defaultValue]    Value to be filled in in new entries,
     *                              0 by default.
     * @private
     */
            function _resize(array, size, dim, defaultValue) {
                if (!(array instanceof Array)) {
                    throw new TypeError("Array expected");
                }
                var len = array.length, newLen = size[dim];
                if (len != newLen) {
                    if (newLen > array.length) {
                        // enlarge
                        for (var i = array.length; i < newLen; i++) {
                            array[i] = defaultValue ? math.clone(defaultValue) : 0;
                        }
                    } else {
                        // shrink
                        array.length = size[dim];
                    }
                    len = array.length;
                }
                if (dim < size.length - 1) {
                    // recursively validate each child array
                    var dimNext = dim + 1;
                    for (i = 0; i < len; i++) {
                        child = array[i];
                        if (!(child instanceof Array)) {
                            child = [ child ];
                            array[i] = child;
                        }
                        _resize(child, size, dimNext, defaultValue);
                    }
                } else {
                    // last dimension
                    for (i = 0; i < len; i++) {
                        var child = array[i];
                        while (child instanceof Array) {
                            child = child[0];
                        }
                        array[i] = child;
                    }
                }
            }
            /**
     * Resize a multi dimensional array
     * @param {Array} array         Array to be resized
     * @param {Number[]} size       Array with the size of each dimension
     * @param {*} [defaultValue]    Value to be filled in in new entries,
     *                              0 by default
     */
            util.resize = function resize(array, size, defaultValue) {
                // TODO: what to do with scalars, when size=[] ?
                // check the type of size
                if (!(size instanceof Array)) {
                    throw new TypeError("Size must be an array (size is " + math["typeof"](size) + ")");
                }
                // check whether size contains positive integers
                size.forEach(function(value) {
                    if (!isNumber(value) || !isInteger(value) || value < 0) {
                        throw new TypeError("Invalid size, must contain positive integers " + "(size: " + util.formatArray(size) + ")");
                    }
                });
                var hasZeros = size.indexOf(0) != -1;
                if (hasZeros) {
                    // array where all dimensions are zero
                    size.forEach(function(value) {
                        if (value != 0) {
                            throw new RangeError("Invalid size, all dimensions must be " + "either zero or non-zero (size: " + util.formatArray(size) + ")");
                        }
                    });
                }
                // recursively resize
                _resize(array, size, 0, defaultValue);
            };
            return util;
        }();
        /**
 * Utility functions for Booleans
 */
        /**
 * Test whether value is a Boolean
 * @param {*} value
 * @return {Boolean} isBoolean
 */
        function isBoolean(value) {
            return value instanceof Boolean || typeof value == "boolean";
        }
        /**
 * @constructor Complex
 *
 * A complex value can be constructed in the following ways:
 *     var a = new Complex();
 *     var b = new Complex(re, im);
 *     var c = Complex.parse(str);
 *
 * Example usage:
 *     var a = new Complex(3, -4);      // 3 - 4i
 *     a.re = 5;                        // a = 5 - 4i
 *     var i = a.im;                    // -4;
 *     var b = Complex.parse('2 + 6i'); // 2 + 6i
 *     var c = new Complex();           // 0 + 0i
 *     var d = math.add(a, b);          // 5 + 2i
 *
 * @param {Number} re       The real part of the complex value
 * @param {Number} [im]     The imaginary part of the complex value
 */
        function Complex(re, im) {
            if (!(this instanceof Complex)) {
                throw new SyntaxError("Complex constructor must be called with the new operator");
            }
            if (re != null && !isNumber(re) || im != null && !isNumber(im)) {
                throw new TypeError("Two numbers expected in Complex constructor");
            }
            this.re = re || 0;
            this.im = im || 0;
        }
        math.type.Complex = Complex;
        // Complex parser methods in a closure
        (function() {
            var text, index, c;
            function skipWhitespace() {
                while (c == " " || c == "	") {
                    next();
                }
            }
            function isDigitDot(c) {
                return c >= "0" && c <= "9" || c == ".";
            }
            function isDigit(c) {
                return c >= "0" && c <= "9";
            }
            function next() {
                index++;
                c = text.charAt(index);
            }
            function revert(oldIndex) {
                index = oldIndex;
                c = text.charAt(index);
            }
            function parseNumber() {
                var number = "";
                var oldIndex;
                oldIndex = index;
                if (c == "+") {
                    next();
                } else if (c == "-") {
                    number += c;
                    next();
                }
                if (!isDigitDot(c)) {
                    // a + or - must be followed by a digit
                    revert(oldIndex);
                    return null;
                }
                // get number, can have a single dot
                if (c == ".") {
                    number += c;
                    next();
                    if (!isDigit(c)) {
                        // this is no legal number, it is just a dot
                        revert(oldIndex);
                        return null;
                    }
                } else {
                    while (isDigit(c)) {
                        number += c;
                        next();
                    }
                    if (c == ".") {
                        number += c;
                        next();
                    }
                }
                while (isDigit(c)) {
                    number += c;
                    next();
                }
                // check for scientific notation like "2.3e-4" or "1.23e50"
                if (c == "E" || c == "e") {
                    number += c;
                    next();
                    if (c == "+" || c == "-") {
                        number += c;
                        next();
                    }
                    // Scientific notation MUST be followed by an exponent
                    if (!isDigit(c)) {
                        // this is no legal number, exponent is missing.
                        revert(oldIndex);
                        return null;
                    }
                    while (isDigit(c)) {
                        number += c;
                        next();
                    }
                }
                return number;
            }
            function parseComplex() {
                // check for 'i', '-i', '+i'
                var cnext = text.charAt(index + 1);
                if (c == "I" || c == "i") {
                    next();
                    return "1";
                } else if ((c == "+" || c == "-") && (cnext == "I" || cnext == "i")) {
                    var number = c == "+" ? "1" : "-1";
                    next();
                    next();
                    return number;
                }
                return null;
            }
            /**
     * Parse a complex number from a string. For example Complex.parse("2 + 3i")
     * will return a Complex value where re = 2, im = 3.
     * Returns null if provided string does not contain a valid complex number.
     * @param {String} str
     * @returns {Complex | null} complex
     */
            Complex.parse = function parse(str) {
                text = str;
                index = -1;
                c = "";
                if (!isString(text)) {
                    return null;
                }
                next();
                skipWhitespace();
                var first = parseNumber();
                if (first) {
                    if (c == "I" || c == "i") {
                        // pure imaginary number
                        next();
                        skipWhitespace();
                        if (c) {
                            // garbage at the end. not good.
                            return null;
                        }
                        return new Complex(0, Number(first));
                    } else {
                        // complex and real part
                        skipWhitespace();
                        var separator = c;
                        if (separator != "+" && separator != "-") {
                            // pure real number
                            skipWhitespace();
                            if (c) {
                                // garbage at the end. not good.
                                return null;
                            }
                            return new Complex(Number(first), 0);
                        } else {
                            // complex and real part
                            next();
                            skipWhitespace();
                            var second = parseNumber();
                            if (second) {
                                if (c != "I" && c != "i") {
                                    // 'i' missing at the end of the complex number
                                    return null;
                                }
                                next();
                            } else {
                                second = parseComplex();
                                if (!second) {
                                    // imaginary number missing after separator
                                    return null;
                                }
                            }
                            if (separator == "-") {
                                if (second[0] == "-") {
                                    second = "+" + second.substring(1);
                                } else {
                                    second = "-" + second;
                                }
                            }
                            next();
                            skipWhitespace();
                            if (c) {
                                // garbage at the end. not good.
                                return null;
                            }
                            return new Complex(Number(first), Number(second));
                        }
                    }
                } else {
                    // check for 'i', '-i', '+i'
                    first = parseComplex();
                    if (first) {
                        skipWhitespace();
                        if (c) {
                            // garbage at the end. not good.
                            return null;
                        }
                        return new Complex(0, Number(first));
                    }
                }
                return null;
            };
        })();
        /**
 * Test whether value is a Complex value
 * @param {*} value
 * @return {Boolean} isComplex
 */
        function isComplex(value) {
            return value instanceof Complex;
        }
        /**
 * Create a copy of the complex value
 * @return {Complex} clone
 */
        Complex.prototype.clone = function() {
            return new Complex(this.re, this.im);
        };
        /**
 * Get string representation of the Complex value
 * @return {String} str
 */
        Complex.prototype.toString = function() {
            var str = "";
            var strRe = util.formatNumber(this.re, math.options.precision);
            var strIm = util.formatNumber(this.im, math.options.precision);
            if (this.im == 0) {
                // real value
                str = strRe;
            } else if (this.re == 0) {
                // purely complex value
                if (this.im == 1) {
                    str = "i";
                } else if (this.im == -1) {
                    str = "-i";
                } else {
                    str = strIm + "i";
                }
            } else {
                // complex value
                if (this.im > 0) {
                    if (this.im == 1) {
                        str = strRe + " + i";
                    } else {
                        str = strRe + " + " + strIm + "i";
                    }
                } else {
                    if (this.im == -1) {
                        str = strRe + " - i";
                    } else {
                        str = strRe + " - " + util.formatNumber(Math.abs(this.im), math.options.precision) + "i";
                    }
                }
            }
            return str;
        };
        /**
 * @constructor Matrix
 *
 * A Matrix is a wrapper around an Array. A matrix can hold a multi dimensional
 * array. A matrix can be constructed as:
 *     var matrix = new Matrix(data)
 *
 * Matrix contains the functions to resize, get and set values, get the size,
 * clone the matrix and to convert the matrix to a vector, array, or scalar.
 * Furthermore, one can iterate over the matrix using map and forEach.
 * The internal Array of the Matrix can be accessed using the method valueOf.
 *
 * Example usage:
 *     var matrix = new Matrix([[1, 2], [3, 4]);
 *     matix.size();              // [2, 2]
 *     matrix.resize([3, 2], 5);
 *     matrix.valueOf();          // [[1, 2], [3, 4], [5, 5]]
 *     matrix.get([2,1])         // 3
 *
 * @param {Array | Matrix} [data]    A multi dimensional array
 */
        function Matrix(data) {
            if (!(this instanceof Matrix)) {
                throw new SyntaxError("Matrix constructor must be called with the new operator");
            }
            if (data instanceof Matrix || data instanceof Range) {
                // clone data from a Matrix or Range
                this._data = data.toArray();
            } else if (data instanceof Array) {
                // use array as is
                this._data = data;
            } else if (data != null) {
                // unsupported type
                throw new TypeError("Unsupported type of data (" + math["typeof"](data) + ")");
            } else {
                // nothing provided
                this._data = [];
            }
            // verify the size of the array
            this._size = util.size(this._data);
        }
        math.type.Matrix = Matrix;
        /**
 * Get a value or a submatrix of the matrix.
 * @param {Array | Matrix} index    One-based index
 */
        Matrix.prototype.get = function(index) {
            var isScalar;
            if (index instanceof Matrix) {
                // index is scalar when size==[n] or size==[1,1,...]
                isScalar = index.size().length == 1 || !index.size().some(function(i) {
                    return i != 1;
                });
                index = index.valueOf();
            } else if (index instanceof Array) {
                isScalar = !index.some(function(elem) {
                    var size = math.size(elem);
                    return size.length != 0 && size != [ 1 ];
                });
            } else {
                throw new TypeError("Invalid index");
            }
            if (index.length != this._size.length) {
                throw new RangeError("Dimension mismatch " + "(" + index.length + " != " + this._size.length + ")");
            }
            if (isScalar) {
                // return a single value
                switch (index.length) {
                  case 1:
                    return _get(this._data, index[0]);

                  case 2:
                    return _get(_get(this._data, index[0]), index[1]);

                  default:
                    return _getScalar(this._data, index);
                }
            } else {
                // return a submatrix
                switch (index.length) {
                  case 1:
                    return new Matrix(_getSubmatrix1D(this._data, index));

                  case 2:
                    return new Matrix(_getSubmatrix2D(this._data, index));

                  default:
                    return new Matrix(_getSubmatrix(this._data, index, 0));
                }
            }
        };
        /**
 * Get a single value from an array. The method tests whether:
 * - index is a non-negative integer
 * - index does not exceed the dimensions of array
 * @param {Array} array
 * @param {Number} index   One-based index
 * @return {*} value
 * @private
 */
        function _get(array, index) {
            util.validateIndex(index, array.length);
            return array[index - 1];
        }
        /**
 * Get a single value from the matrix. The value will be a copy of the original
 * value in the matrix.
 * Index is not checked for correct number of dimensions.
 * @param {Array} data
 * @param {Number[]} index   One-based index
 * @return {*} scalar
 * @private
 */
        function _getScalar(data, index) {
            index.forEach(function(i) {
                data = _get(data, i);
            });
            return math.clone(data);
        }
        /**
 * Get a submatrix of a one dimensional matrix.
 * Index is not checked for correct number of dimensions.
 * @param {Array} data
 * @param {Array} index         One-based index
 * @return {Array} submatrix
 * @private
 */
        function _getSubmatrix1D(data, index) {
            var current = index[0];
            if (current.map) {
                // array or Range
                return current.map(function(i) {
                    return _get(data, i);
                });
            } else {
                // scalar
                return [ _get(data, current) ];
            }
        }
        /**
 * Get a submatrix of a 2 dimensional matrix.
 * Index is not checked for correct number of dimensions.
 * @param {Array} data
 * @param {Array} index         One-based index
 * @return {Array} submatrix
 * @private
 */
        function _getSubmatrix2D(data, index) {
            var rows = index[0];
            var cols = index[1];
            if (rows.map) {
                if (cols.map) {
                    return rows.map(function(row) {
                        var child = _get(data, row);
                        return cols.map(function(col) {
                            return _get(child, col);
                        });
                    });
                } else {
                    return rows.map(function(row) {
                        return [ _get(_get(data, row), cols) ];
                    });
                }
            } else {
                if (cols.map) {
                    var child = _get(data, rows);
                    return [ cols.map(function(col) {
                        return _get(child, col);
                    }) ];
                } else {
                    return [ [ _get(_get(data, rows), cols) ] ];
                }
            }
        }
        /**
 * Get a submatrix of a multi dimensional matrix.
 * Index is not checked for correct number of dimensions.
 * @param {Array} data
 * @param {Array} index         One-based index
 * @param {number} dim
 * @return {Array} submatrix
 * @private
 */
        function _getSubmatrix(data, index, dim) {
            var last = dim == index.length - 1;
            var current = index[dim];
            var recurse = function(i) {
                var child = _get(data, i);
                return last ? child : _getSubmatrix(child, index, dim + 1);
            };
            if (current.map) {
                // array or Range
                return current.map(recurse);
            } else {
                // scalar
                return [ recurse(current) ];
            }
        }
        /**
 * Replace a value or a submatrix in the matrix.
 * Indexes are one-based.
 * @param {Array | Matrix} index        One-based index
 * @param {*} submatrix
 * @return {Matrix} itself
 */
        Matrix.prototype.set = function(index, submatrix) {
            var isScalar;
            if (index instanceof Matrix) {
                // index is scalar when size==[n] or size==[1,1,...]
                isScalar = index.size().length == 1 || !index.size().some(function(i) {
                    return i != 1;
                });
                index = index.valueOf();
            } else if (index instanceof Array) {
                isScalar = !index.some(function(elem) {
                    var size = math.size(elem);
                    return size.length != 0 && size != [ 1 ];
                });
            } else {
                throw new TypeError("Invalid index");
            }
            if (submatrix instanceof Matrix || submatrix instanceof Range) {
                submatrix = submatrix.valueOf();
            }
            if (index.length < this._size.length) {
                throw new RangeError("Dimension mismatch " + "(" + index.length + " != " + this._size.length + ")");
            }
            if (isScalar) {
                // set a scalar
                // check whether submatrix is no matrix/array
                if (math.size(submatrix).length != 0) {
                    throw new TypeError("Scalar value expected");
                }
                switch (index.length) {
                  case 1:
                    _setScalar1D(this._data, this._size, index, submatrix);
                    break;

                  case 2:
                    _setScalar2D(this._data, this._size, index, submatrix);
                    break;

                  default:
                    _setScalar(this._data, this._size, index, submatrix);
                    break;
                }
            } else {
                // set a submatrix
                var size = this._size.concat();
                _setSubmatrix(this._data, size, index, 0, submatrix);
                if (!util.deepEqual(this._size, size)) {
                    _init(this._data);
                    this.resize(size);
                }
            }
            return this;
        };
        /**
 * Replace a single value in an array. The method tests whether index is a
 * non-negative integer
 * @param {Array} array
 * @param {Number} index   One-based index
 * @param {*} value
 * @private
 */
        function _set(array, index, value) {
            util.validateIndex(index);
            if (value instanceof Array) {
                throw new TypeError("Dimension mismatch, value expected instead of array");
            }
            array[index - 1] = value;
        }
        /**
 * Replace a single value in a multi dimensional matrix
 * @param {Array} data
 * @param {Number[]} size
 * @param {Number[]} index  One-based index
 * @param {*} value
 * @private
 */
        function _setScalar(data, size, index, value) {
            var resized = false;
            if (index.length > size.length) {
                // dimension added
                resized = true;
            }
            for (var i = 0; i < index.length; i++) {
                var index_i = index[i];
                util.validateIndex(index_i);
                if (size[i] == null || index_i > size[i]) {
                    size[i] = index_i;
                    resized = true;
                }
            }
            if (resized) {
                util.resize(data, size, 0);
            }
            var len = size.length;
            index.forEach(function(v, i) {
                if (i < len - 1) {
                    data = data[v - 1];
                } else {
                    data[v - 1] = value;
                }
            });
        }
        /**
 * Replace a single value in a one dimensional matrix
 * @param {Array} data
 * @param {Number[]} size
 * @param {Number[]} index      One-based index
 * @param {*} value
 * @private
 */
        function _setScalar1D(data, size, index, value) {
            var row = index[0];
            util.validateIndex(row);
            if (row > size[0]) {
                util.resize(data, [ row ], 0);
                size[0] = row;
            }
            data[row - 1] = value;
        }
        /**
 * Replace a single value in a two dimensional matrix
 * @param {Array} data
 * @param {Number[]} size
 * @param {Number[]} index  One-based index
 * @param {*} value
 * @private
 */
        function _setScalar2D(data, size, index, value) {
            var row = index[0];
            var col = index[1];
            util.validateIndex(row);
            util.validateIndex(col);
            var resized = false;
            if (row > (size[0] || 0)) {
                size[0] = row;
                resized = true;
            }
            if (col > (size[1] || 0)) {
                size[1] = col;
                resized = true;
            }
            if (resized) {
                util.resize(data, size, 0);
            }
            data[row - 1][col - 1] = value;
        }
        /**
 * Replace a submatrix of a multi dimensional matrix.
 * @param {Array} data
 * @param {Array} size
 * @param {Array} index     One-based index
 * @param {number} dim
 * @param {Array} submatrix
 * @private
 */
        function _setSubmatrix(data, size, index, dim, submatrix) {
            var last = dim == index.length - 1;
            var current = index[dim];
            var recurse = function(dataIndex, subIndex) {
                if (last) {
                    _set(data, dataIndex, submatrix[subIndex]);
                    if (dataIndex > (size[dim] || 0)) {
                        size[dim] = dataIndex;
                    }
                } else {
                    var child = data[dataIndex - 1];
                    // one-based index
                    if (!(child instanceof Array)) {
                        data[dataIndex - 1] = child = [ child ];
                    }
                    if (dataIndex > (size[dim] || 0)) {
                        size[dim] = dataIndex;
                    }
                    _setSubmatrix(child, size, index, dim + 1, submatrix[subIndex]);
                }
            };
            if (current.map) {
                // array or Range
                var len = current.size && current.size() || current.length;
                if (len != submatrix.length) {
                    throw new RangeError("Dimensions mismatch " + "(" + len + " != " + submatrix.length + ")");
                }
                current.map(recurse);
            } else {
                // scalar
                recurse(current, 0);
            }
        }
        /**
 * Recursively initialize all undefined values in the array with zeros
 * @param array
 * @private
 */
        function _init(array) {
            for (var i = 0, len = array.length; i < len; i++) {
                var value = array[i];
                if (value instanceof Array) {
                    _init(value);
                } else if (value == undefined) {
                    array[i] = 0;
                }
            }
        }
        /**
 * Resize the matrix
 * @param {Number[]} size
 * @param {*} [defaultValue]        Default value, filled in on new entries.
 *                                  If not provided, the vector will be filled
 *                                  with zeros.
 */
        Matrix.prototype.resize = function(size, defaultValue) {
            util.resize(this._data, size, defaultValue);
            this._size = math.clone(size);
        };
        /**
 * Create a clone of the matrix
 * @return {Matrix} clone
 */
        Matrix.prototype.clone = function() {
            var matrix = new Matrix();
            matrix._data = math.clone(this._data);
            matrix._size = math.clone(this._size);
            return matrix;
        };
        /**
 * Retrieve the size of the matrix.
 * The size of the matrix will be validated too
 * @returns {Number[]} size
 */
        Matrix.prototype.size = function() {
            return this._size;
        };
        /**
 * Create a new matrix with the results of the callback function executed on
 * each entry of the matrix.
 * @param {function} callback   The callback method is invoked with three
 *                              parameters: the value of the element, the index
 *                              of the element, and the Matrix being traversed.
 * @return {Matrix} matrix
 */
        Matrix.prototype.map = function(callback) {
            var me = this;
            var matrix = new Matrix();
            var index = [];
            var recurse = function(value, dim) {
                if (value instanceof Array) {
                    return value.map(function(child, i) {
                        index[dim] = i + 1;
                        // one-based index
                        return recurse(child, dim + 1);
                    });
                } else {
                    return callback(value, index, me);
                }
            };
            matrix._data = recurse(this._data, 0);
            matrix._size = math.clone(this._size);
            return matrix;
        };
        /**
 * Execute a callback method on each entry of the matrix.
 * @param {function} callback   The callback method is invoked with three
 *                              parameters: the value of the element, the index
 *                              of the element, and the Matrix being traversed.
 */
        Matrix.prototype.forEach = function(callback) {
            var me = this;
            var index = [];
            var recurse = function(value, dim) {
                if (value instanceof Array) {
                    value.forEach(function(child, i) {
                        index[dim] = i + 1;
                        // one-based index
                        recurse(child, dim + 1);
                    });
                } else {
                    callback(value, index, me);
                }
            };
            recurse(this._data, 0);
        };
        /**
 * Create a scalar with a copy of the data of the Matrix
 * Will return null if the matrix does not consist of a scalar value
 * @return {* | null} scalar
 */
        Matrix.prototype.toScalar = function() {
            var scalar = this._data;
            while (scalar instanceof Array && scalar.length == 1) {
                scalar = scalar[0];
            }
            if (scalar instanceof Array) {
                return null;
            } else {
                return math.clone(scalar);
            }
        };
        /**
 * Test whether the matrix is a scalar.
 * @return {boolean} isScalar
 */
        Matrix.prototype.isScalar = function() {
            return this._size.every(function(s) {
                return s <= 1;
            });
        };
        /**
 * Create a vector with a copy of the data of the Matrix
 * Returns null if the Matrix does not contain a vector
 *
 * A matrix is a vector when it has 0 or 1 dimensions, or has multiple
 * dimensions where maximum one of the dimensions has a size larger than 1.
 * return {Array | null} vector
 */
        Matrix.prototype.toVector = function() {
            var count = 0;
            var dim = undefined;
            var index = [];
            this._size.forEach(function(length, i) {
                if (length > 1) {
                    count++;
                    dim = i;
                }
                index[i] = 0;
            });
            if (count == 0) {
                // scalar or empty
                var scalar = this.toScalar();
                if (scalar) {
                    return [ scalar ];
                } else {
                    return [];
                }
            } else if (count == 1) {
                // valid vector
                var vector = [];
                var recurse = function(data) {
                    if (data instanceof Array) {
                        data.forEach(recurse);
                    } else {
                        vector.push(data);
                    }
                };
                recurse(this._data);
                return vector;
            } else {
                // count > 1, this is no vector
                return null;
            }
        };
        /**
 * Test if the matrix contains a vector.
 * A matrix is a vector when it has 0 or 1 dimensions, or has multiple
 * dimensions where maximum one of the dimensions has a size larger than 1.
 * return {boolean} isVector
 */
        Matrix.prototype.isVector = function() {
            var count = 0;
            this._size.forEach(function(length) {
                if (length > 1) {
                    count++;
                }
            });
            return count <= 1;
        };
        /**
 * Create an Array with a copy of the data of the Matrix
 * @returns {Array} array
 */
        Matrix.prototype.toArray = function() {
            return math.clone(this._data);
        };
        /**
 * Get the primitive value of the Matrix: a multidimensional array
 * @returns {Array} array
 */
        Matrix.prototype.valueOf = function() {
            return this._data;
        };
        /**
 * Get a string representation of the matrix
 * @returns {String} str
 */
        Matrix.prototype.toString = function() {
            return math.format(this._data);
        };
        /**
 * Utility functions for Numbers
 */
        /**
 * Test whether value is a Number
 * @param {*} value
 * @return {Boolean} isNumber
 */
        function isNumber(value) {
            return value instanceof Number || typeof value == "number";
        }
        /**
 * Check if a number is integer
 * @param {Number} value
 * @return {Boolean} isInteger
 */
        function isInteger(value) {
            return value == Math.round(value);
        }
        /**
 * @constructor Range
 * Create a range. A range works similar to an Array, with functions like
 * forEach and map. However, a Range object is very cheap to create compared to
 * a large Array with indexes, as it stores only a start, step and end value of
 * the range.
 *
 * A range can be constructed as:
 *     var a = new Range(start, step, end);
 *
 * To get the result of the range:
 *     range.forEach(function (x) {
 *         console.log(x);
 *     });
 *     range.map(function (x) {
 *         return math.sin(x);
 *     });
 *     range.toArray();
 *
 * Example usage:
 *     var c = new Range(2, 1, 5);      // 2:1:5
 *     c.toArray();                     // [2, 3, 4, 5]
 *     var d = new Range(2, -1, -2);    // 2:-1:-2
 *     d.toArray();                     // [2, 1, 0, -1, -2]
 *
 * @param {Number} start
 * @param {Number} step
 * @param {Number} end
 */
        function Range(start, step, end) {
            if (!(this instanceof Range)) {
                throw new SyntaxError("Range constructor must be called with the new operator");
            }
            if (start != null && !isNumber(start)) {
                throw new TypeError("Parameter start must be a number");
            }
            if (end != null && !isNumber(end)) {
                throw new TypeError("Parameter end must be a number");
            }
            if (step != null && !isNumber(step)) {
                throw new TypeError("Parameter step must be a number");
            }
            this.start = start != null ? start : 0;
            this.end = end != null ? end : 0;
            this.step = step != null ? step : 1;
        }
        math.type.Range = Range;
        /**
 * Parse a string into a range,
 * The string contains the start, optional step, and end, separated by a colon.
 * If the string does not contain a valid range, null is returned.
 * For example str='0:2:10'.
 * @param {String} str
 * @return {Range | null} range
 */
        Range.parse = function(str) {
            if (!isString(str)) {
                return null;
            }
            var args = str.split(":");
            var nums = args.map(function(arg) {
                return Number(arg);
            });
            var invalid = nums.some(function(num) {
                return isNaN(num);
            });
            if (invalid) {
                return null;
            }
            switch (nums.length) {
              case 2:
                return new Range(nums[0], 1, nums[1]);

              case 3:
                return new Range(nums[0], nums[1], nums[2]);

              default:
                return null;
            }
        };
        /**
 * Create a clone of the range
 * @return {Range} clone
 */
        Range.prototype.clone = function() {
            return new Range(this.start, this.step, this.end);
        };
        /**
 * Retrieve the size of the range.
 * @returns {Number[]} size
 */
        Range.prototype.size = function() {
            var len = 0, start = Number(this.start), step = Number(this.step), end = Number(this.end), diff = end - start;
            if (math.sign(step) == math.sign(diff)) {
                len = Math.floor(diff / step) + 1;
            } else if (diff == 0) {
                len = 1;
            }
            if (isNaN(len)) {
                len = 0;
            }
            return [ len ];
        };
        /**
 * Execute a callback function for each value in the range.
 * @param {function} callback   The callback method is invoked with three
 *                              parameters: the value of the element, the index
 *                              of the element, and the Matrix being traversed.
 */
        Range.prototype.forEach = function(callback) {
            var x = Number(this.start);
            var step = Number(this.step);
            var end = Number(this.end);
            var i = 0;
            if (step > 0) {
                while (x <= end) {
                    callback(x, i, this);
                    x += step;
                    i++;
                }
            } else if (step < 0) {
                while (x >= end) {
                    callback(x, i, this);
                    x += step;
                    i++;
                }
            }
        };
        /**
 * Execute a callback function for each value in the Range, and return the
 * results as an array
 * @param {function} callback   The callback method is invoked with three
 *                              parameters: the value of the element, the index
 *                              of the element, and the Matrix being traversed.
 * @returns {Array} array
 */
        Range.prototype.map = function(callback) {
            var array = [];
            this.forEach(function(value, index, obj) {
                array[index] = callback(value, index, obj);
            });
            return array;
        };
        /**
 * Create a Matrix with a copy of the Ranges data
 * @return {Matrix} matrix
 */
        Range.prototype.toMatrix = function() {
            return new Matrix(this.toArray());
        };
        /**
 * Create an Array with a copy of the Ranges data
 * @returns {Array} array
 */
        Range.prototype.toArray = function() {
            var array = [];
            this.forEach(function(value, index) {
                array[index] = value;
            });
            return array;
        };
        /**
 * Create an array with a copy of the Ranges data.
 * This method is equal to Range.toArray, and is available for compatibility
 * with Matrix.
 * @return {Array} vector
 */
        Range.prototype.toVector = Range.prototype.toArray;
        /**
 * Test if the range contains a vector. For a range, this is always the case
 * return {boolean} isVector
 */
        Range.prototype.isVector = function() {
            return true;
        };
        /**
 * Create a scalar with a copy of the data of the Range
 * Will return null if the range does not consist of a scalar value
 * @return {* | null} scalar
 */
        Range.prototype.toScalar = function() {
            var array = this.toArray();
            if (array.length == 1) {
                return array[0];
            } else {
                return null;
            }
        };
        /**
 * Test whether the matrix is a scalar.
 * @return {boolean} isScalar
 */
        Range.prototype.isScalar = function() {
            return this.size()[0] == 1;
        };
        /**
 * Get the primitive value of the Range, a one dimensional array
 * @returns {Array} array
 */
        Range.prototype.valueOf = function() {
            // TODO: implement a caching mechanism for range.valueOf()
            return this.toArray();
        };
        /**
 * Get the string representation of the range, for example '2:5' or '0:0.2:10'
 * @returns {String} str
 */
        Range.prototype.toString = function() {
            var str = math.format(Number(this.start));
            if (this.step != 1) {
                str += ":" + math.format(Number(this.step));
            }
            str += ":" + math.format(Number(this.end));
            return str;
        };
        /**
 * @constructor math.type.Selector
 * Wrap any value in a Selector, allowing to perform chained operations on
 * the value.
 *
 * All methods available in the math.js library can be called upon the selector,
 * and then will be evaluated with the value itself as first argument.
 * The selector can be closed by executing selector.done(), which will return
 * the final value.
 *
 * The Selector has a number of special functions:
 * - done()     Finalize the chained operation and return the selectors value.
 * - valueOf()  The same as done()
 * - toString() Executes math.format() onto the selectors value, returning
 *              a string representation of the value.
 * - get(...)   Get a subset of the selectors value. Useful for example for
 *              matrices and arrays.
 * - set(...)   Replace a subset of the selectors value. Useful for example for
 *              matrices and arrays.
 *
 * @param {*} [value]
 */
        math.type.Selector = function Selector(value) {
            if (!(this instanceof math.type.Selector)) {
                throw new SyntaxError("Selector constructor must be called with the new operator");
            }
            if (value instanceof math.type.Selector) {
                this.value = value.value;
            } else {
                this.value = value || undefined;
            }
        };
        math.type.Selector.prototype = {
            /**
     * Close the selector. Returns the final value.
     * Does the same as method valueOf()
     * @returns {*} value
     */
            done: function() {
                return this.value;
            },
            /**
     * Get a submatrix or subselection from current value.
     * Only applicable when the current value has a method get.
     */
            get: function(index) {
                var value = this.value;
                if (!value) {
                    throw Error("Selector value is undefined");
                }
                return new math.type.Selector(math.subset(value, index));
            },
            /**
     * Set a submatrix or subselection on current value.
     * Only applicable when the current value has a method set.
     */
            set: function(index, replacement) {
                var value = this.value;
                if (!value) {
                    throw Error("Selector value is undefined");
                }
                return new math.type.Selector(math.subset(value, index, replacement));
            },
            /**
     * Close the selector. Returns the final value.
     * Does the same as method done()
     * @returns {*} value
     */
            valueOf: function() {
                return this.value;
            },
            /**
     * Get the string representation of the value in the selector
     * @returns {String}
     */
            toString: function() {
                return math.format(this.value);
            }
        };
        /**
 * Create a proxy method for the selector
 * @param {String} name
 * @param {*} value       The value or function to be proxied
 */
        function createSelectorProxy(name, value) {
            var Selector = math.type.Selector;
            var slice = Array.prototype.slice;
            if (typeof value === "function") {
                // a function
                Selector.prototype[name] = function() {
                    var args = [ this.value ].concat(slice.call(arguments, 0));
                    return new Selector(value.apply(this, args));
                };
            } else {
                // a constant
                Selector.prototype[name] = new Selector(value);
            }
        }
        /**
 * Utility functions for Strings
 */
        /**
 * Test whether value is a String
 * @param {*} value
 * @return {Boolean} isString
 */
        function isString(value) {
            return value instanceof String || typeof value == "string";
        }
        /**
 * @constructor Unit
 *
 * A unit can be constructed in the following ways:
 *     var a = new Unit(value, unit);
 *     var b = new Unit(null, unit);
 *     var c = Unit.parse(str);
 *
 * Example usage:
 *     var a = new Unit(5, 'cm');               // 50 mm
 *     var b = Unit.parse('23 kg');             // 23 kg
 *     var c = math.in(a, new Unit(null, 'm');  // 0.05 m
 *
 * @param {Number} [value]  A value like 5.2
 * @param {String} [unit]   A unit like "cm" or "inch"
 */
        function Unit(value, unit) {
            if (!(this instanceof Unit)) {
                throw new Error("Unit constructor must be called with the new operator");
            }
            if (value != null && !isNumber(value)) {
                throw new TypeError("First parameter in Unit constructor must be a number");
            }
            if (unit != null && !isString(unit)) {
                throw new TypeError("Second parameter in Unit constructor must be a string");
            }
            if (unit != null) {
                // find the unit and prefix from the string
                var res = _findUnit(unit);
                if (!res) {
                    throw new SyntaxError('String "' + unit + '" is no unit');
                }
                this.unit = res.unit;
                this.prefix = res.prefix;
            } else {
                this.unit = Unit.UNIT_NONE;
                this.prefix = Unit.PREFIX_NONE;
            }
            if (value != null) {
                this.value = this._normalize(value);
                this.fixPrefix = false;
            } else {
                this.value = null;
                this.fixPrefix = true;
            }
        }
        math.type.Unit = Unit;
        (function() {
            var text, index, c;
            function skipWhitespace() {
                while (c == " " || c == "	") {
                    next();
                }
            }
            function isDigitDot(c) {
                return c >= "0" && c <= "9" || c == ".";
            }
            function isDigit(c) {
                return c >= "0" && c <= "9";
            }
            function next() {
                index++;
                c = text.charAt(index);
            }
            function revert(oldIndex) {
                index = oldIndex;
                c = text.charAt(index);
            }
            function parseNumber() {
                var number = "";
                var oldIndex;
                oldIndex = index;
                if (c == "+") {
                    next();
                } else if (c == "-") {
                    number += c;
                    next();
                }
                if (!isDigitDot(c)) {
                    // a + or - must be followed by a digit
                    revert(oldIndex);
                    return null;
                }
                // get number, can have a single dot
                if (c == ".") {
                    number += c;
                    next();
                    if (!isDigit(c)) {
                        // this is no legal number, it is just a dot
                        revert(oldIndex);
                        return null;
                    }
                } else {
                    while (isDigit(c)) {
                        number += c;
                        next();
                    }
                    if (c == ".") {
                        number += c;
                        next();
                    }
                }
                while (isDigit(c)) {
                    number += c;
                    next();
                }
                // check for scientific notation like "2.3e-4" or "1.23e50"
                if (c == "E" || c == "e") {
                    number += c;
                    next();
                    if (c == "+" || c == "-") {
                        number += c;
                        next();
                    }
                    // Scientific notation MUST be followed by an exponent
                    if (!isDigit(c)) {
                        // this is no legal number, exponent is missing.
                        revert(oldIndex);
                        return null;
                    }
                    while (isDigit(c)) {
                        number += c;
                        next();
                    }
                }
                return number;
            }
            function parseUnit() {
                var unit = "";
                skipWhitespace();
                while (c && c != " " && c != "	") {
                    unit += c;
                    next();
                }
                return unit || null;
            }
            /**
     * Parse a string into a unit. Returns null if the provided string does not
     * contain a valid unit.
     * @param {String} str        A string like "5.2 inch", "4e2 kg"
     * @return {Unit | null} unit
     */
            Unit.parse = function parse(str) {
                text = str;
                index = -1;
                c = "";
                if (!isString(text)) {
                    return null;
                }
                next();
                skipWhitespace();
                var value = parseNumber();
                var unit;
                if (value) {
                    unit = parseUnit();
                    next();
                    skipWhitespace();
                    if (c) {
                        // garbage at the end. not good.
                        return null;
                    }
                    if (value && unit) {
                        return new Unit(Number(value), unit);
                    }
                } else {
                    unit = parseUnit();
                    next();
                    skipWhitespace();
                    if (c) {
                        // garbage at the end. not good.
                        return null;
                    }
                    return new Unit(null, unit);
                }
                return null;
            };
        })();
        /**
 * Test whether value is of type Unit
 * @param {*} value
 * @return {Boolean} isUnit
 */
        function isUnit(value) {
            return value instanceof Unit;
        }
        /**
 * create a copy of this unit
 * @return {Unit} clone
 */
        Unit.prototype.clone = function() {
            var clone = new Unit();
            for (var p in this) {
                if (this.hasOwnProperty(p)) {
                    clone[p] = this[p];
                }
            }
            return clone;
        };
        /**
 * Normalize a value, based on its currently set unit
 * @param {Number} value
 * @return {Number} normalized value
 * @private
 */
        Unit.prototype._normalize = function(value) {
            return (value + this.unit.offset) * this.unit.value * this.prefix.value;
        };
        /**
 * Unnormalize a value, based on its currently set unit
 * @param {Number} value
 * @param {Number} [prefixValue]    Optional prefix value to be used
 * @return {Number} unnormalized value
 * @private
 */
        Unit.prototype._unnormalize = function(value, prefixValue) {
            if (prefixValue == undefined) {
                return value / this.unit.value / this.prefix.value - this.unit.offset;
            } else {
                return value / this.unit.value / prefixValue - this.unit.offset;
            }
        };
        /**
 * Find a unit from a string
 * @param {String} str              A string like 'cm' or 'inch'
 * @returns {Object | null} result  When found, an object with fields unit and
 *                                  prefix is returned. Else, null is returned.
 * @private
 */
        function _findUnit(str) {
            var UNITS = Unit.UNITS;
            for (var i = 0, iMax = UNITS.length; i < iMax; i++) {
                var UNIT = UNITS[i];
                if (util.endsWith(str, UNIT.name)) {
                    var prefixLen = str.length - UNIT.name.length;
                    var prefixName = str.substring(0, prefixLen);
                    var prefix = UNIT.prefixes[prefixName];
                    if (prefix !== undefined) {
                        // store unit, prefix, and value
                        return {
                            unit: UNIT,
                            prefix: prefix
                        };
                    }
                }
            }
            return null;
        }
        /**
 * Test if the given expression is a unit.
 * The unit can have a prefix but cannot have a value.
 * @param {String} unit   A plain unit without value. Can have prefix, like "cm"
 * @return {Boolean}      true if the given string is a unit
 */
        Unit.isPlainUnit = function(unit) {
            return _findUnit(unit) != null;
        };
        /**
 * check if this unit has given base unit
 * @param {Unit.BASE_UNITS} base
 */
        Unit.prototype.hasBase = function(base) {
            if (this.unit.base === undefined) {
                return base === undefined;
            }
            return this.unit.base === base;
        };
        /**
 * Check if this unit has a base equal to another base
 * @param {Unit} other
 * @return {Boolean} true if equal base
 */
        Unit.prototype.equalBase = function(other) {
            return this.unit.base === other.unit.base;
        };
        /**
 * Check if this unit equals another unit
 * @param {Unit} other
 * @return {Boolean} true if both units are equal
 */
        Unit.prototype.equals = function(other) {
            return this.equalBase(other) && this.value == other.value;
        };
        /**
 * Create a clone of this unit with a representation
 * @param {String | Unit} plainUnit   A plain unit, without value. Can have prefix, like "cm"
 * @returns {Unit} unit having fixed, specified unit
 */
        Unit.prototype["in"] = function(plainUnit) {
            var other;
            if (isString(plainUnit)) {
                other = new Unit(null, plainUnit);
                if (!this.equalBase(other)) {
                    throw new Error("Units do not match");
                }
                other.value = this.value;
                return other;
            } else if (plainUnit instanceof Unit) {
                if (!this.equalBase(plainUnit)) {
                    throw new Error("Units do not match");
                }
                if (plainUnit.value != null) {
                    throw new Error("Cannot convert to a unit with a value");
                }
                if (plainUnit.unit == null) {
                    throw new Error("Unit expected on the right hand side of function in");
                }
                other = plainUnit.clone();
                other.value = this.value;
                other.fixPrefix = true;
                return other;
            } else {
                throw new Error("String or Unit expected as parameter");
            }
        };
        /**
 * Return the value of the unit when represented with given plain unit
 * @param {String | Unit} plainUnit    For example 'cm' or 'inch'
 * @return {Number} value
 */
        Unit.prototype.toNumber = function(plainUnit) {
            var other = this["in"](plainUnit);
            var prefix = this.fixPrefix ? other._bestPrefix() : other.prefix;
            return other._unnormalize(other.value, prefix.value);
        };
        /**
 * Get string representation
 * @return {String}
 */
        Unit.prototype.toString = function() {
            var value, str;
            if (!this.fixPrefix) {
                var bestPrefix = this._bestPrefix();
                value = this._unnormalize(this.value, bestPrefix.value);
                str = this.value != null ? util.formatNumber(value, math.options.precision) + " " : "";
                str += bestPrefix.name + this.unit.name;
            } else {
                value = this._unnormalize(this.value);
                str = this.value != null ? util.formatNumber(value, math.options.precision) + " " : "";
                str += this.prefix.name + this.unit.name;
            }
            return str;
        };
        /**
 * Calculate the best prefix using current value.
 * @returns {Object} prefix
 * @private
 */
        Unit.prototype._bestPrefix = function() {
            // find the best prefix value (resulting in the value of which
            // the absolute value of the log10 is closest to zero,
            // though with a little offset of 1.2 for nicer values: you get a
            // sequence 1mm 100mm 500mm 0.6m 1m 10m 100m 500m 0.6km 1km ...
            var absValue = Math.abs(this.value / this.unit.value);
            var bestPrefix = Unit.PREFIX_NONE;
            var bestDiff = Math.abs(Math.log(absValue / bestPrefix.value) / Math.LN10 - 1.2);
            var prefixes = this.unit.prefixes;
            for (var p in prefixes) {
                if (prefixes.hasOwnProperty(p)) {
                    var prefix = prefixes[p];
                    if (prefix.scientific) {
                        var diff = Math.abs(Math.log(absValue / prefix.value) / Math.LN10 - 1.2);
                        if (diff < bestDiff) {
                            bestPrefix = prefix;
                            bestDiff = diff;
                        }
                    }
                }
            }
            return bestPrefix;
        };
        Unit.PREFIXES = {
            NONE: {
                "": {
                    name: "",
                    value: 1,
                    scientific: true
                }
            },
            SHORT: {
                "": {
                    name: "",
                    value: 1,
                    scientific: true
                },
                da: {
                    name: "da",
                    value: 10,
                    scientific: false
                },
                h: {
                    name: "h",
                    value: 100,
                    scientific: false
                },
                k: {
                    name: "k",
                    value: 1e3,
                    scientific: true
                },
                M: {
                    name: "M",
                    value: 1e6,
                    scientific: true
                },
                G: {
                    name: "G",
                    value: 1e9,
                    scientific: true
                },
                T: {
                    name: "T",
                    value: 1e12,
                    scientific: true
                },
                P: {
                    name: "P",
                    value: 1e15,
                    scientific: true
                },
                E: {
                    name: "E",
                    value: 1e18,
                    scientific: true
                },
                Z: {
                    name: "Z",
                    value: 1e21,
                    scientific: true
                },
                Y: {
                    name: "Y",
                    value: 1e24,
                    scientific: true
                },
                d: {
                    name: "d",
                    value: .1,
                    scientific: false
                },
                c: {
                    name: "c",
                    value: .01,
                    scientific: false
                },
                m: {
                    name: "m",
                    value: .001,
                    scientific: true
                },
                // 'µ': {'name': 'µ', 'value': 1e-6, 'scientific': true},
                u: {
                    name: "u",
                    value: 1e-6,
                    scientific: true
                },
                n: {
                    name: "n",
                    value: 1e-9,
                    scientific: true
                },
                p: {
                    name: "p",
                    value: 1e-12,
                    scientific: true
                },
                f: {
                    name: "f",
                    value: 1e-15,
                    scientific: true
                },
                a: {
                    name: "a",
                    value: 1e-18,
                    scientific: true
                },
                z: {
                    name: "z",
                    value: 1e-21,
                    scientific: true
                },
                y: {
                    name: "y",
                    value: 1e-24,
                    scientific: true
                }
            },
            LONG: {
                "": {
                    name: "",
                    value: 1,
                    scientific: true
                },
                deca: {
                    name: "deca",
                    value: 10,
                    scientific: false
                },
                hecto: {
                    name: "hecto",
                    value: 100,
                    scientific: false
                },
                kilo: {
                    name: "kilo",
                    value: 1e3,
                    scientific: true
                },
                mega: {
                    name: "mega",
                    value: 1e6,
                    scientific: true
                },
                giga: {
                    name: "giga",
                    value: 1e9,
                    scientific: true
                },
                tera: {
                    name: "tera",
                    value: 1e12,
                    scientific: true
                },
                peta: {
                    name: "peta",
                    value: 1e15,
                    scientific: true
                },
                exa: {
                    name: "exa",
                    value: 1e18,
                    scientific: true
                },
                zetta: {
                    name: "zetta",
                    value: 1e21,
                    scientific: true
                },
                yotta: {
                    name: "yotta",
                    value: 1e24,
                    scientific: true
                },
                deci: {
                    name: "deci",
                    value: .1,
                    scientific: false
                },
                centi: {
                    name: "centi",
                    value: .01,
                    scientific: false
                },
                milli: {
                    name: "milli",
                    value: .001,
                    scientific: true
                },
                micro: {
                    name: "micro",
                    value: 1e-6,
                    scientific: true
                },
                nano: {
                    name: "nano",
                    value: 1e-9,
                    scientific: true
                },
                pico: {
                    name: "pico",
                    value: 1e-12,
                    scientific: true
                },
                femto: {
                    name: "femto",
                    value: 1e-15,
                    scientific: true
                },
                atto: {
                    name: "atto",
                    value: 1e-18,
                    scientific: true
                },
                zepto: {
                    name: "zepto",
                    value: 1e-21,
                    scientific: true
                },
                yocto: {
                    name: "yocto",
                    value: 1e-24,
                    scientific: true
                }
            },
            BINARY_SHORT: {
                "": {
                    name: "",
                    value: 1,
                    scientific: true
                },
                k: {
                    name: "k",
                    value: 1024,
                    scientific: true
                },
                M: {
                    name: "M",
                    value: Math.pow(1024, 2),
                    scientific: true
                },
                G: {
                    name: "G",
                    value: Math.pow(1024, 3),
                    scientific: true
                },
                T: {
                    name: "T",
                    value: Math.pow(1024, 4),
                    scientific: true
                },
                P: {
                    name: "P",
                    value: Math.pow(1024, 5),
                    scientific: true
                },
                E: {
                    name: "E",
                    value: Math.pow(1024, 6),
                    scientific: true
                },
                Z: {
                    name: "Z",
                    value: Math.pow(1024, 7),
                    scientific: true
                },
                Y: {
                    name: "Y",
                    value: Math.pow(1024, 8),
                    scientific: true
                },
                Ki: {
                    name: "Ki",
                    value: 1024,
                    scientific: true
                },
                Mi: {
                    name: "Mi",
                    value: Math.pow(1024, 2),
                    scientific: true
                },
                Gi: {
                    name: "Gi",
                    value: Math.pow(1024, 3),
                    scientific: true
                },
                Ti: {
                    name: "Ti",
                    value: Math.pow(1024, 4),
                    scientific: true
                },
                Pi: {
                    name: "Pi",
                    value: Math.pow(1024, 5),
                    scientific: true
                },
                Ei: {
                    name: "Ei",
                    value: Math.pow(1024, 6),
                    scientific: true
                },
                Zi: {
                    name: "Zi",
                    value: Math.pow(1024, 7),
                    scientific: true
                },
                Yi: {
                    name: "Yi",
                    value: Math.pow(1024, 8),
                    scientific: true
                }
            },
            BINARY_LONG: {
                "": {
                    name: "",
                    value: 1,
                    scientific: true
                },
                kilo: {
                    name: "kilo",
                    value: 1024,
                    scientific: true
                },
                mega: {
                    name: "mega",
                    value: Math.pow(1024, 2),
                    scientific: true
                },
                giga: {
                    name: "giga",
                    value: Math.pow(1024, 3),
                    scientific: true
                },
                tera: {
                    name: "tera",
                    value: Math.pow(1024, 4),
                    scientific: true
                },
                peta: {
                    name: "peta",
                    value: Math.pow(1024, 5),
                    scientific: true
                },
                exa: {
                    name: "exa",
                    value: Math.pow(1024, 6),
                    scientific: true
                },
                zetta: {
                    name: "zetta",
                    value: Math.pow(1024, 7),
                    scientific: true
                },
                yotta: {
                    name: "yotta",
                    value: Math.pow(1024, 8),
                    scientific: true
                },
                kibi: {
                    name: "kibi",
                    value: 1024,
                    scientific: true
                },
                mebi: {
                    name: "mebi",
                    value: Math.pow(1024, 2),
                    scientific: true
                },
                gibi: {
                    name: "gibi",
                    value: Math.pow(1024, 3),
                    scientific: true
                },
                tebi: {
                    name: "tebi",
                    value: Math.pow(1024, 4),
                    scientific: true
                },
                pebi: {
                    name: "pebi",
                    value: Math.pow(1024, 5),
                    scientific: true
                },
                exi: {
                    name: "exi",
                    value: Math.pow(1024, 6),
                    scientific: true
                },
                zebi: {
                    name: "zebi",
                    value: Math.pow(1024, 7),
                    scientific: true
                },
                yobi: {
                    name: "yobi",
                    value: Math.pow(1024, 8),
                    scientific: true
                }
            }
        };
        Unit.PREFIX_NONE = {
            name: "",
            value: 1,
            scientific: true
        };
        Unit.BASE_UNITS = {
            NONE: {},
            LENGTH: {},
            // meter
            MASS: {},
            // kilogram
            TIME: {},
            // second
            CURRENT: {},
            // ampere
            TEMPERATURE: {},
            // kelvin
            LUMINOUS_INTENSITY: {},
            // candela
            AMOUNT_OF_SUBSTANCE: {},
            // mole
            FORCE: {},
            // Newton
            SURFACE: {},
            // m2
            VOLUME: {},
            // m3
            ANGLE: {},
            // rad
            BIT: {}
        };
        var BASE_UNITS = Unit.BASE_UNITS;
        var PREFIXES = Unit.PREFIXES;
        Unit.BASE_UNIT_NONE = {};
        Unit.UNIT_NONE = {
            name: "",
            base: Unit.BASE_UNIT_NONE,
            value: 1,
            offset: 0
        };
        Unit.UNITS = [ // length
        {
            name: "meter",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, {
            name: "inch",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .0254,
            offset: 0
        }, {
            name: "foot",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .3048,
            offset: 0
        }, {
            name: "yard",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .9144,
            offset: 0
        }, {
            name: "mile",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 1609.344,
            offset: 0
        }, {
            name: "link",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .201168,
            offset: 0
        }, {
            name: "rod",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 5.02921,
            offset: 0
        }, {
            name: "chain",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 20.1168,
            offset: 0
        }, {
            name: "angstrom",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 1e-10,
            offset: 0
        }, {
            name: "m",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, //{'name': 'in', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.0254, 'offset': 0}, not supported, In is an operator
        {
            name: "ft",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .3048,
            offset: 0
        }, {
            name: "yd",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .9144,
            offset: 0
        }, {
            name: "mi",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 1609.344,
            offset: 0
        }, {
            name: "li",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: .201168,
            offset: 0
        }, {
            name: "rd",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 5.02921,
            offset: 0
        }, {
            name: "ch",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 20.1168,
            offset: 0
        }, {
            name: "mil",
            base: BASE_UNITS.LENGTH,
            prefixes: PREFIXES.NONE,
            value: 254e-7,
            offset: 0
        }, // 1/1000 inch
        // Surface
        {
            name: "m2",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, {
            name: "sqin",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: 64516e-8,
            offset: 0
        }, // 645.16 mm2
        {
            name: "sqft",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: .09290304,
            offset: 0
        }, // 0.09290304 m2
        {
            name: "sqyd",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: .83612736,
            offset: 0
        }, // 0.83612736 m2
        {
            name: "sqmi",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: 2589988.110336,
            offset: 0
        }, // 2.589988110336 km2
        {
            name: "sqrd",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: 25.29295,
            offset: 0
        }, // 25.29295 m2
        {
            name: "sqch",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: 404.6873,
            offset: 0
        }, // 404.6873 m2
        {
            name: "sqmil",
            base: BASE_UNITS.SURFACE,
            prefixes: PREFIXES.NONE,
            value: 6.4516e-10,
            offset: 0
        }, // 6.4516 * 10^-10 m2
        // Volume
        {
            name: "m3",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, {
            name: "L",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.SHORT,
            value: .001,
            offset: 0
        }, // litre
        {
            name: "litre",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.LONG,
            value: .001,
            offset: 0
        }, {
            name: "cuin",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 16387064e-12,
            offset: 0
        }, // 1.6387064e-5 m3
        {
            name: "cuft",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .028316846592,
            offset: 0
        }, // 28.316 846 592 L
        {
            name: "cuyd",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .764554857984,
            offset: 0
        }, // 764.554 857 984 L
        {
            name: "teaspoon",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 5e-6,
            offset: 0
        }, // 5 mL
        {
            name: "tablespoon",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 15e-6,
            offset: 0
        }, // 15 mL
        //{'name': 'cup', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.000240, 'offset': 0}, // 240 mL  // not possible, we have already another cup
        // Liquid volume
        {
            name: "minim",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 6.161152e-8,
            offset: 0
        }, // 0.06161152 mL
        {
            name: "fluiddram",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 36966911e-13,
            offset: 0
        }, // 3.696691 mL
        {
            name: "fluidounce",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 2957353e-11,
            offset: 0
        }, // 29.57353 mL
        {
            name: "gill",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0001182941,
            offset: 0
        }, // 118.2941 mL
        {
            name: "cup",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0002365882,
            offset: 0
        }, // 236.5882 mL
        {
            name: "pint",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0004731765,
            offset: 0
        }, // 473.1765 mL
        {
            name: "quart",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0009463529,
            offset: 0
        }, // 946.3529 mL
        {
            name: "gallon",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .003785412,
            offset: 0
        }, // 3.785412 L
        {
            name: "beerbarrel",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .1173478,
            offset: 0
        }, // 117.3478 L
        {
            name: "oilbarrel",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .1589873,
            offset: 0
        }, // 158.9873 L
        {
            name: "hogshead",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .238481,
            offset: 0
        }, // 238.4810 L
        //{'name': 'min', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.00000006161152, 'offset': 0}, // 0.06161152 mL // min is already in use as minute
        {
            name: "fldr",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 36966911e-13,
            offset: 0
        }, // 3.696691 mL
        {
            name: "floz",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: 2957353e-11,
            offset: 0
        }, // 29.57353 mL
        {
            name: "gi",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0001182941,
            offset: 0
        }, // 118.2941 mL
        {
            name: "cp",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0002365882,
            offset: 0
        }, // 236.5882 mL
        {
            name: "pt",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0004731765,
            offset: 0
        }, // 473.1765 mL
        {
            name: "qt",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .0009463529,
            offset: 0
        }, // 946.3529 mL
        {
            name: "gal",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .003785412,
            offset: 0
        }, // 3.785412 L
        {
            name: "bbl",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .1173478,
            offset: 0
        }, // 117.3478 L
        {
            name: "obl",
            base: BASE_UNITS.VOLUME,
            prefixes: PREFIXES.NONE,
            value: .1589873,
            offset: 0
        }, // 158.9873 L
        //{'name': 'hogshead', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.2384810, 'offset': 0}, // 238.4810 L // TODO: hh?
        // Mass
        {
            name: "g",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.SHORT,
            value: .001,
            offset: 0
        }, {
            name: "gram",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.LONG,
            value: .001,
            offset: 0
        }, {
            name: "ton",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.SHORT,
            value: 907.18474,
            offset: 0
        }, {
            name: "tonne",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.SHORT,
            value: 1e3,
            offset: 0
        }, {
            name: "grain",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: 6479891e-11,
            offset: 0
        }, {
            name: "dram",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .0017718451953125,
            offset: 0
        }, {
            name: "ounce",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .028349523125,
            offset: 0
        }, {
            name: "poundmass",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .45359237,
            offset: 0
        }, {
            name: "hundredweight",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: 45.359237,
            offset: 0
        }, {
            name: "stick",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .115,
            offset: 0
        }, {
            name: "gr",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: 6479891e-11,
            offset: 0
        }, {
            name: "dr",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .0017718451953125,
            offset: 0
        }, {
            name: "oz",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .028349523125,
            offset: 0
        }, {
            name: "lbm",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: .45359237,
            offset: 0
        }, {
            name: "cwt",
            base: BASE_UNITS.MASS,
            prefixes: PREFIXES.NONE,
            value: 45.359237,
            offset: 0
        }, // Time
        {
            name: "s",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, {
            name: "min",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 60,
            offset: 0
        }, {
            name: "h",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 3600,
            offset: 0
        }, {
            name: "seconds",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, {
            name: "second",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, {
            name: "sec",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, {
            name: "minutes",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 60,
            offset: 0
        }, {
            name: "minute",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 60,
            offset: 0
        }, {
            name: "hours",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 3600,
            offset: 0
        }, {
            name: "hour",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 3600,
            offset: 0
        }, {
            name: "day",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 86400,
            offset: 0
        }, {
            name: "days",
            base: BASE_UNITS.TIME,
            prefixes: PREFIXES.NONE,
            value: 86400,
            offset: 0
        }, // Angles
        {
            name: "rad",
            base: BASE_UNITS.ANGLE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, {
            name: "deg",
            base: BASE_UNITS.ANGLE,
            prefixes: PREFIXES.NONE,
            value: .017453292519943295,
            offset: 0
        }, // deg = rad / (2*pi) * 360 = rad / 0.017453292519943295769236907684888
        {
            name: "grad",
            base: BASE_UNITS.ANGLE,
            prefixes: PREFIXES.NONE,
            value: .015707963267948967,
            offset: 0
        }, // grad = rad / (2*pi) * 400  = rad / 0.015707963267948966192313216916399
        {
            name: "cycle",
            base: BASE_UNITS.ANGLE,
            prefixes: PREFIXES.NONE,
            value: 6.283185307179586,
            offset: 0
        }, // cycle = rad / (2*pi) = rad / 6.2831853071795864769252867665793
        // Electric current
        {
            name: "A",
            base: BASE_UNITS.CURRENT,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, {
            name: "ampere",
            base: BASE_UNITS.CURRENT,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, // Temperature
        // K(C) = °C + 273.15
        // K(F) = (°F + 459.67) / 1.8
        // K(R) = °R / 1.8
        {
            name: "K",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, {
            name: "degC",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 273.15
        }, {
            name: "degF",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1 / 1.8,
            offset: 459.67
        }, {
            name: "degR",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1 / 1.8,
            offset: 0
        }, {
            name: "kelvin",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, {
            name: "celsius",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 273.15
        }, {
            name: "fahrenheit",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1 / 1.8,
            offset: 459.67
        }, {
            name: "rankine",
            base: BASE_UNITS.TEMPERATURE,
            prefixes: PREFIXES.NONE,
            value: 1 / 1.8,
            offset: 0
        }, // amount of substance
        {
            name: "mol",
            base: BASE_UNITS.AMOUNT_OF_SUBSTANCE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, {
            name: "mole",
            base: BASE_UNITS.AMOUNT_OF_SUBSTANCE,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, // luminous intensity
        {
            name: "cd",
            base: BASE_UNITS.LUMINOUS_INTENSITY,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, {
            name: "candela",
            base: BASE_UNITS.LUMINOUS_INTENSITY,
            prefixes: PREFIXES.NONE,
            value: 1,
            offset: 0
        }, // TODO: units STERADIAN
        //{'name': 'sr', 'base': BASE_UNITS.STERADIAN, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0},
        //{'name': 'steradian', 'base': BASE_UNITS.STERADIAN, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0},
        // Force
        {
            name: "N",
            base: BASE_UNITS.FORCE,
            prefixes: PREFIXES.SHORT,
            value: 1,
            offset: 0
        }, {
            name: "newton",
            base: BASE_UNITS.FORCE,
            prefixes: PREFIXES.LONG,
            value: 1,
            offset: 0
        }, {
            name: "lbf",
            base: BASE_UNITS.FORCE,
            prefixes: PREFIXES.NONE,
            value: 4.4482216152605,
            offset: 0
        }, {
            name: "poundforce",
            base: BASE_UNITS.FORCE,
            prefixes: PREFIXES.NONE,
            value: 4.4482216152605,
            offset: 0
        }, // Binary
        {
            name: "b",
            base: BASE_UNITS.BIT,
            prefixes: PREFIXES.BINARY_SHORT,
            value: 1,
            offset: 0
        }, {
            name: "bits",
            base: BASE_UNITS.BIT,
            prefixes: PREFIXES.BINARY_LONG,
            value: 1,
            offset: 0
        }, {
            name: "B",
            base: BASE_UNITS.BIT,
            prefixes: PREFIXES.BINARY_SHORT,
            value: 8,
            offset: 0
        }, {
            name: "bytes",
            base: BASE_UNITS.BIT,
            prefixes: PREFIXES.BINARY_LONG,
            value: 8,
            offset: 0
        } ];
        /**
 * mathjs constants
 */
        math.E = Math.E;
        math.LN2 = Math.LN2;
        math.LN10 = Math.LN10;
        math.LOG2E = Math.LOG2E;
        math.LOG10E = Math.LOG10E;
        math.PI = Math.PI;
        math.SQRT1_2 = Math.SQRT1_2;
        math.SQRT2 = Math.SQRT2;
        math.I = new Complex(0, 1);
        // lower case constants
        math.pi = math.PI;
        math.e = math.E;
        math.i = math.I;
        /**
 * Helper methods for functions
 */
        /**
 * Create a TypeError with message:
 *      'Function <fn> does not support a parameter of type <type>';
 * @param {String} name   Function name
 * @param {*} value1
 * @param {*} [value2]
 * @return {TypeError | Error} error
 */
        function newUnsupportedTypeError(name, value1, value2) {
            var msg = undefined;
            if (arguments.length == 2) {
                var t = math["typeof"](value1);
                msg = "Function " + name + "(" + t + ") not supported";
            } else if (arguments.length > 2) {
                var types = [];
                for (var i = 1; i < arguments.length; i++) {
                    types.push(math["typeof"](arguments[i]));
                }
                msg = "Function " + name + "(" + types.join(", ") + ") not supported";
            } else {
                msg = "Unsupported parameter in function " + name;
            }
            return new TypeError(msg);
        }
        /**
 * Create a syntax error with the message:
 *     'Wrong number of arguments in function <fn> (<count> provided, <min>-<max> expected)'
 * @param {String} name   Function name
 * @param {Number} count  Actual argument count
 * @param {Number} min    Minimum required argument count
 * @param {Number} [max]  Maximum required argument count
 */
        function newArgumentsError(name, count, min, max) {
            var msg = "Wrong number of arguments in function " + name + " (" + count + " provided, " + min + (max != undefined ? "-" + max : "") + " expected)";
            return new SyntaxError(msg);
        }
        /**
 * Node
 */
        function Node() {}
        math.expr.node.Node = Node;
        /**
 * Evaluate the node
 * @return {*} result
 */
        Node.prototype.eval = function() {
            throw new Error("Cannot evaluate a Node interface");
        };
        /**
 * Find any node in the node tree matching given filter. For example, to
 * find all nodes of type SymbolNode having name 'x':
 *
 *     var results = Node.find({
 *         type: SymbolNode,
 *         properties: {
 *             name: 'x'
 *         }
 *     });
 *
 * @param {Object} filter       Available parameters:
 *                              {Function} type
 *                              {Object<String, String>} properties
 * @return {Node[]} nodes       An array with nodes matching given filter criteria
 */
        Node.prototype.find = function(filter) {
            return this.match(filter) ? [ this ] : [];
        };
        /**
 * Test if this object matches given filter
 * @param {Object} filter       Available parameters:
 *                              {Function} type
 *                              {Object<String, String>} properties
 * @return {Boolean} matches    True if there is a match
 */
        Node.prototype.match = function(filter) {
            var match = true;
            if (filter) {
                if (filter.type && !(this instanceof filter.type)) {
                    match = false;
                }
                if (match && filter.properties) {
                    for (var prop in filter.properties) {
                        if (filter.properties.hasOwnProperty(prop)) {
                            if (this[prop] != filter.properties[prop]) {
                                match = false;
                                break;
                            }
                        }
                    }
                }
            }
            return match;
        };
        /**
 * Get string representation
 * @return {String}
 */
        Node.prototype.toString = function() {
            return "";
        };
        /**
 * @constructor ConstantNode
 * @param {*} value
 * @extends {Node}
 */
        function ConstantNode(value) {
            this.value = value;
        }
        ConstantNode.prototype = new Node();
        math.expr.node.ConstantNode = ConstantNode;
        /**
 * Evaluate the constant (just return it)
 * @return {*} value
 */
        ConstantNode.prototype.eval = function() {
            return this.value;
        };
        /**
 * Get string representation
 * @return {String} str
 */
        ConstantNode.prototype.toString = function() {
            return math.format(this.value || null);
        };
        /**
 * @constructor OperatorNode
 * An operator with two arguments, like 2+3
 * @param {String} name     Function name, for example '+'
 * @param {function} fn     Function, for example math.add
 * @param {Node[]} params   Parameters
 */
        function OperatorNode(name, fn, params) {
            this.name = name;
            this.fn = fn;
            this.params = params;
        }
        OperatorNode.prototype = new Node();
        math.expr.node.OperatorNode = OperatorNode;
        /**
 * Evaluate the parameters
 * @return {*} result
 */
        OperatorNode.prototype.eval = function() {
            return this.fn.apply(this, this.params.map(function(param) {
                return param.eval();
            }));
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        OperatorNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search in parameters
            var params = this.params;
            if (params) {
                for (var i = 0, len = params.length; i < len; i++) {
                    nodes = nodes.concat(params[i].find(filter));
                }
            }
            return nodes;
        };
        /**
 * Get string representation
 * @return {String} str
 */
        OperatorNode.prototype.toString = function() {
            var params = this.params;
            // special case: unary minus
            if (this.fn === math.unaryminus) {
                return "-" + params[0].toString();
            }
            switch (params.length) {
              case 1:
                // for example '5!'
                return params[0].toString() + this.name;

              case 2:
                // for example '2+3'
                var lhs = params[0].toString();
                if (params[0] instanceof OperatorNode) {
                    lhs = "(" + lhs + ")";
                }
                var rhs = params[1].toString();
                if (params[1] instanceof OperatorNode) {
                    rhs = "(" + rhs + ")";
                }
                return lhs + " " + this.name + " " + rhs;

              default:
                // this should occur. format as a function call
                return this.name + "(" + this.params.join(", ") + ")";
            }
        };
        /**
 * @constructor SymbolNode
 * A symbol node can hold and resolve a symbol
 * @param {String} name
 * @param {math.expr.Scope} scope
 * @extends {Node}
 */
        function SymbolNode(name, scope) {
            this.name = name;
            this.scope = scope;
        }
        SymbolNode.prototype = new Node();
        math.expr.node.SymbolNode = SymbolNode;
        /**
 * Evaluate the symbol. Throws an error when the symbol is undefined.
 * @return {*} result
 * @override
 */
        SymbolNode.prototype.eval = function() {
            // return the value of the symbol
            var value = this.scope.get(this.name);
            if (value === undefined) {
                throw new Error("Undefined symbol " + this.name);
            }
            return value;
        };
        /**
 * Get string representation
 * @return {String} str
 * @override
 */
        SymbolNode.prototype.toString = function() {
            return this.name;
        };
        /**
 * @constructor ParamsNode
 * invoke a list with parameters on the results of a node
 * @param {Node} object
 * @param {Node[]} params
 * @param {Scope[]} paramScopes     A scope for every parameter, where the
 *                                  index variable 'end' can be defined.
 */
        function ParamsNode(object, params, paramScopes) {
            this.object = object;
            this.params = params;
            this.paramScopes = paramScopes;
            // check whether any of the params expressions uses the context symbol 'end'
            this.hasContextParams = false;
            if (params) {
                var filter = {
                    type: math.type.SymbolNode,
                    properties: {
                        name: "end"
                    }
                };
                for (var i = 0, len = params.length; i < len; i++) {
                    if (params[i].find(filter).length > 0) {
                        this.hasContextParams = true;
                        break;
                    }
                }
            }
        }
        ParamsNode.prototype = new Node();
        math.expr.node.ParamsNode = ParamsNode;
        /**
 * Evaluate the parameters
 * @return {*} result
 */
        ParamsNode.prototype.eval = function() {
            var i, len;
            // evaluate the object
            var object = this.object;
            if (object == undefined) {
                throw new Error("Node undefined");
            }
            var obj = object.eval();
            // evaluate the values of context parameter 'end' when needed
            if (this.hasContextParams) {
                var paramScopes = this.paramScopes, size;
                if (obj.size) {
                    size = obj.size();
                } else if (obj.length !== undefined) {
                    size = [ obj.length ];
                } else {
                    size = [];
                }
                if (paramScopes && size) {
                    for (i = 0, len = this.params.length; i < len; i++) {
                        var paramScope = paramScopes[i];
                        if (paramScope) {
                            paramScope.set("end", size[i]);
                        }
                    }
                }
            }
            // evaluate the parameters
            var params = this.params, results = [];
            for (i = 0, len = this.params.length; i < len; i++) {
                results[i] = params[i].eval();
            }
            if (typeof obj === "function") {
                // invoke a function with the parameters
                return obj.apply(this, results);
            } else {
                // get a subset of the object
                return math.subset(obj, results);
            }
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        ParamsNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search object
            if (this.object) {
                nodes = nodes.concat(this.object.find(filter));
            }
            // search in parameters
            var params = this.params;
            if (params) {
                for (var i = 0, len = params.length; i < len; i++) {
                    nodes = nodes.concat(params[i].find(filter));
                }
            }
            return nodes;
        };
        /**
 * Get string representation
 * @return {String} str
 */
        ParamsNode.prototype.toString = function() {
            // format the parameters like "(2, 4.2)"
            var str = this.object ? this.object.toString() : "";
            if (this.params) {
                str += "(" + this.params.join(", ") + ")";
            }
            return str;
        };
        /**
 * @constructor MatrixNode
 * Holds an 2-dimensional array with nodes
 * @param {Array[]} nodes    2 dimensional array with nodes
 * @extends {Node}
 */
        function MatrixNode(nodes) {
            this.nodes = nodes || [];
        }
        MatrixNode.prototype = new Node();
        math.expr.node.MatrixNode = MatrixNode;
        (function() {
            /**
     * Evaluate the array
     * @return {Matrix} results
     * @override
     */
            MatrixNode.prototype.eval = function() {
                // evaluate all nodes in the 2d array, and merge the results into a matrix
                var nodes = this.nodes, results = [], mergeNeeded = false;
                for (var r = 0, rows = nodes.length; r < rows; r++) {
                    var nodes_r = nodes[r];
                    var results_r = [];
                    for (var c = 0, cols = nodes_r.length; c < cols; c++) {
                        var results_rc = nodes_r[c].eval();
                        if (results_rc instanceof Matrix || results_rc instanceof Range || results_rc instanceof Array) {
                            mergeNeeded = true;
                        }
                        results_r[c] = results_rc;
                    }
                    results[r] = results_r;
                }
                if (mergeNeeded) {
                    results = merge(results);
                }
                return new Matrix(results);
            };
            /**
     * Find all nodes matching given filter
     * @param {Object} filter  See Node.find for a description of the filter options
     * @returns {Node[]} nodes
     */
            MatrixNode.prototype.find = function(filter) {
                var results = [];
                // check itself
                if (this.match(filter)) {
                    results.push(this);
                }
                // search in all nodes
                var nodes = this.nodes;
                for (var r = 0, rows = nodes.length; r < rows; r++) {
                    var nodes_r = nodes[r];
                    for (var c = 0, cols = nodes_r.length; c < cols; c++) {
                        results = results.concat(nodes_r[c].find(filter));
                    }
                }
                return results;
            };
            /**
     * Merge nested Matrices in a two dimensional Array.
     * @param {Array} array    Two-dimensional array containing Matrices
     * @return {Array} merged  The merged array (two-dimensional)
     */
            function merge(array) {
                var merged = [];
                var rows = array.length;
                for (var r = 0; r < rows; r++) {
                    var array_r = array[r];
                    var cols = array_r.length;
                    var submatrix = null;
                    var submatrixRows = null;
                    for (var c = 0; c < cols; c++) {
                        var entry = math.clone(array_r[c]);
                        var size;
                        if (entry instanceof Matrix) {
                            // get the data from the matrix
                            size = entry.size();
                            entry = entry.valueOf();
                            if (size.length == 1) {
                                entry = [ entry ];
                                size = [ 1, size[0] ];
                            } else if (size.length > 2) {
                                throw new Error("Cannot merge a multi dimensional matrix");
                            }
                        } else if (entry instanceof Range) {
                            // change range into an 1xn matrix
                            entry = [ entry.valueOf() ];
                            size = [ 1, entry[0].length ];
                        } else if (entry instanceof Array) {
                            // change array into a 1xn matrix
                            size = [ 1, entry.length ];
                            entry = [ entry ];
                        } else {
                            // change scalar into a 1x1 matrix
                            size = [ 1, 1 ];
                            entry = [ [ entry ] ];
                        }
                        // check the height of this row
                        if (submatrix == null) {
                            // first entry
                            submatrix = entry;
                            submatrixRows = size[0];
                        } else if (size[0] == submatrixRows) {
                            // merge
                            for (var s = 0; s < submatrixRows; s++) {
                                submatrix[s] = submatrix[s].concat(entry[s]);
                            }
                        } else {
                            // no good...
                            throw new Error("Dimension mismatch " + "(" + size[0] + " != " + submatrixRows + ")");
                        }
                    }
                    // merge the submatrix
                    merged = merged.concat(submatrix);
                }
                return merged;
            }
            /**
     * Get string representation
     * @return {String} str
     * @override
     */
            MatrixNode.prototype.toString = function() {
                return util.formatArray(this.nodes);
            };
        })();
        /**
 * @constructor BlockNode
 * Holds a set with nodes
 * @extends {Node}
 */
        function BlockNode() {
            this.params = [];
            this.visible = [];
        }
        BlockNode.prototype = new Node();
        math.expr.node.BlockNode = BlockNode;
        /**
 * Add a parameter
 * @param {Node} param
 * @param {Boolean} [visible]   true by default
 */
        BlockNode.prototype.add = function(param, visible) {
            var index = this.params.length;
            this.params[index] = param;
            this.visible[index] = visible != undefined ? visible : true;
        };
        /**
 * Evaluate the set
 * @return {*[]} results
 * @override
 */
        BlockNode.prototype.eval = function() {
            // evaluate the parameters
            var results = [];
            for (var i = 0, iMax = this.params.length; i < iMax; i++) {
                var result = this.params[i].eval();
                if (this.visible[i]) {
                    results.push(result);
                }
            }
            return results;
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        BlockNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search in parameters
            var params = this.params;
            if (params) {
                for (var i = 0, len = params.length; i < len; i++) {
                    nodes = nodes.concat(params[i].find(filter));
                }
            }
            return nodes;
        };
        /**
 * Get string representation
 * @return {String} str
 * @override
 */
        BlockNode.prototype.toString = function() {
            var strings = [];
            for (var i = 0, iMax = this.params.length; i < iMax; i++) {
                if (this.visible[i]) {
                    strings.push("\n  " + this.params[i].toString());
                }
            }
            return "[" + strings.join(",") + "\n]";
        };
        /**
 * @constructor AssignmentNode
 * Define a symbol, like "a = 3.2"
 *
 * @param {String} name                 Symbol name
 * @param {Node} expr                   The expression defining the symbol
 * @param {math.expr.Scope} scope       Scope to store the result
 */
        function AssignmentNode(name, expr, scope) {
            this.name = name;
            this.expr = expr;
            this.scope = scope;
        }
        AssignmentNode.prototype = new Node();
        math.expr.node.AssignmentNode = AssignmentNode;
        /**
 * Evaluate the assignment
 * @return {*} result
 */
        AssignmentNode.prototype.eval = function() {
            if (this.expr === undefined) {
                throw new Error("Undefined symbol " + this.name);
            }
            var result = this.expr.eval();
            this.scope.set(this.name, result);
            return result;
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        AssignmentNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search in expression
            if (this.expr) {
                nodes = nodes.concat(this.expr.find(filter));
            }
            return nodes;
        };
        /**
 * Get string representation
 * @return {String}
 */
        AssignmentNode.prototype.toString = function() {
            return this.name + " = " + this.expr.toString();
        };
        /**
 * @constructor UpdateNode
 * Update a symbol value, like a(2,3) = 4.5
 *
 * @param {String} name                 Symbol name
 * @param {Node[] | undefined} params   One or more parameters
 * @param {Scope[]}  paramScopes        A scope for every parameter, where the
 *                                      index variable 'end' can be defined.
 * @param {Node} expr                   The expression defining the symbol
 * @param {math.expr.Scope} scope       Scope to store the result
 */
        function UpdateNode(name, params, paramScopes, expr, scope) {
            this.name = name;
            this.params = params;
            this.paramScopes = paramScopes;
            this.expr = expr;
            this.scope = scope;
            // check whether any of the params expressions uses the context symbol 'end'
            this.hasContextParams = false;
            var filter = {
                type: math.type.SymbolNode,
                properties: {
                    name: "end"
                }
            };
            for (var i = 0, len = params.length; i < len; i++) {
                if (params[i].find(filter).length > 0) {
                    this.hasContextParams = true;
                    break;
                }
            }
        }
        UpdateNode.prototype = new Node();
        math.expr.node.UpdateNode = UpdateNode;
        /**
 * Evaluate the assignment
 * @return {*} result
 */
        UpdateNode.prototype.eval = function() {
            if (this.expr === undefined) {
                throw new Error("Undefined symbol " + this.name);
            }
            var result;
            var params = this.params;
            // test if definition is currently undefined
            var prevResult = this.scope.get(this.name);
            if (prevResult == undefined) {
                throw new Error("Undefined symbol " + this.name);
            }
            // evaluate the values of context parameter 'end' when needed
            if (this.hasContextParams) {
                var paramScopes = this.paramScopes, size;
                if (prevResult.size) {
                    size = prevResult.size();
                } else if (prevResult.length !== undefined) {
                    size = [ prevResult.length ];
                } else {
                    size = [];
                }
                if (paramScopes && size) {
                    for (var i = 0, len = this.params.length; i < len; i++) {
                        var paramScope = paramScopes[i];
                        if (paramScope) {
                            paramScope.set("end", size[i]);
                        }
                    }
                }
            }
            // change part of a matrix, for example "a=[]", "a(2,3)=4.5"
            var paramResults = [];
            this.params.forEach(function(param) {
                paramResults.push(param.eval());
            });
            var exprResult = this.expr.eval();
            // replace subset
            result = math.subset(prevResult, paramResults, exprResult);
            this.scope.set(this.name, result);
            return result;
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        UpdateNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search in parameters
            var params = this.params;
            if (params) {
                for (var i = 0, len = params.length; i < len; i++) {
                    nodes = nodes.concat(params[i].find(filter));
                }
            }
            // search in expression
            if (this.expr) {
                nodes = nodes.concat(this.expr.find(filter));
            }
            return nodes;
        };
        /**
 * Get string representation
 * @return {String}
 */
        UpdateNode.prototype.toString = function() {
            var str = "";
            str += this.name;
            if (this.params && this.params.length) {
                str += "(" + this.params.join(", ") + ")";
            }
            str += " = ";
            str += this.expr.toString();
            return str;
        };
        /**
 * @constructor FunctionNode
 * Function assignment
 *
 * @param {String} name                     Function name
 * @param {String[]} variables              Variable names
 * @param {Node} expr                       The function expression
 * @param {math.expr.Scope} functionScope   Scope in which to write variable
 *                                          values
 * @param {math.expr.Scope} scope           Scope to store the resulting
 *                                          function assignment
 */
        function FunctionNode(name, variables, expr, functionScope, scope) {
            this.name = name;
            this.variables = variables;
            this.scope = scope;
            // create function
            this.fn = function() {
                var num = variables ? variables.length : 0;
                // validate correct number of arguments
                if (arguments.length != num) {
                    throw newArgumentsError(name, arguments.length, num);
                }
                // fill in the provided arguments in the functionScope variables
                for (var i = 0; i < num; i++) {
                    functionScope.set(variables[i], arguments[i]);
                }
                // evaluate the expression
                return expr.eval();
            };
            this.fn.toString = function() {
                // TODO: what to return as toString?
                return name + "(" + variables.join(", ") + ")";
            };
        }
        FunctionNode.prototype = new Node();
        math.expr.node.FunctionNode = FunctionNode;
        /**
 * Evaluate the function assignment
 * @return {function} fn
 */
        FunctionNode.prototype.eval = function() {
            // put the definition in the scope
            this.scope.set(this.name, this.fn);
            return this.fn;
        };
        /**
 * Find all nodes matching given filter
 * @param {Object} filter  See Node.find for a description of the filter options
 * @returns {Node[]} nodes
 */
        FunctionNode.prototype.find = function(filter) {
            var nodes = [];
            // check itself
            if (this.match(filter)) {
                nodes.push(this);
            }
            // search in expression
            if (this.expr) {
                nodes = nodes.concat(this.expr.find(filter));
            }
            return nodes;
        };
        /**
 * get string representation
 * @return {String} str
 */
        FunctionNode.prototype.toString = function() {
            return this.fn.toString();
        };
        /**
 * Scope
 * A scope stores values of symbols: variables and functions.
 *
 * Syntax:
 *     var scope = new math.expr.Scope();
 *     var scope = new math.expr.Scope(parentScope);
 *     var scope = new math.expr.Scope(symbols);
 *     var scope = new math.expr.Scope(parentScope, symbols);
 *
 * Where:
 *     {math.expr.Scope} parentScope    Scope will be linked to a parent scope,
 *                                      which is traversed when resolving
 *                                      symbols.
 *     {Object} symbols                 A custom object that will be used to
 *                                      resolve and store variables.
 *
 * @constructor math.expr.Scope
 * @param {...} [args]
 */
        math.expr.Scope = function Scope(args) {
            /** @type {math.expr.Scope} */
            this.parentScope = null;
            /** @type {math.expr.Scope[]} */
            this.subScopes = null;
            /** @type {Object.<String, *>} */
            this.symbols = {};
            // variables and functions
            /** @type {Object.<String, Object>} */
            this.cache = {};
            // cache, referring to the scope.symbols object where
            // a variable was last found
            // read first argument (can be parentScope or symbols map)
            if (arguments.length > 0) {
                var arg0 = arguments[0];
                if (arg0 instanceof math.expr.Scope) {
                    this.parentScope = arg0;
                } else if (arg0 instanceof Object) {
                    this.symbols = arg0;
                }
            }
            // read second argument (can be symbols map)
            if (arguments.length > 1) {
                var arg1 = arguments[1];
                if (arg1 instanceof Object) {
                    this.symbols = arg1;
                }
            }
        };
        math.expr.Scope.prototype = {
            /**
     * Create a sub scope
     * The variables in a sub scope are not accessible from the parent scope
     * @return {math.expr.Scope} subScope
     */
            createSubScope: function() {
                var subScope = new math.expr.Scope(this);
                if (!this.subScopes) {
                    this.subScopes = [];
                }
                this.subScopes.push(subScope);
                return subScope;
            },
            /**
     * Get a symbol value by name.
     * Returns undefined if the symbol is not found in this scope or any of
     * its parent scopes.
     * @param {String} name
     * @returns {* | undefined} value
     */
            get: function(name) {
                var value;
                // check itself
                value = this.symbols[name];
                if (value !== undefined) {
                    return value;
                }
                // read from cache
                var symbols = this.cache[name];
                if (symbols) {
                    return symbols[name];
                }
                // check parent scope
                var parent = this.parentScope;
                while (parent) {
                    value = parent.symbols[name];
                    if (value !== undefined) {
                        this.cache[name] = parent.symbols;
                        return value;
                    }
                    parent = parent.parentScope;
                }
                // check math namespace
                value = math[name];
                if (value !== undefined) {
                    this.cache[name] = math;
                    return value;
                }
                // check if name is a unit
                if (Unit.isPlainUnit(name)) {
                    value = new Unit(null, name);
                    this.cache[name] = {};
                    this.cache[name][name] = value;
                    return value;
                }
                return undefined;
            },
            /**
     * Test whether this scope contains a symbol (will not check parent scopes)
     * @param {String} name
     * @return {Boolean} hasSymbol
     */
            has: function(name) {
                return this.symbols[name] !== undefined;
            },
            /**
     * Set a symbol value
     * @param {String} name
     * @param {*} value
     * @return {*} value
     */
            set: function(name, value) {
                return this.symbols[name] = value;
            },
            /**
     * Remove a symbol by name
     * @param {String} name
     */
            remove: function(name) {
                delete this.symbols[name];
            },
            /**
     * Clear all symbols in this scope, its sub scopes, and clear the cache.
     * Parent scopes will not be cleared.
     */
            clear: function() {
                var symbols = this.symbols;
                for (var name in symbols) {
                    if (symbols.hasOwnProperty(name)) {
                        delete symbols[name];
                    }
                }
                if (this.subScopes) {
                    var subScopes = this.subScopes;
                    for (var i = 0, iMax = subScopes.length; i < iMax; i++) {
                        subScopes[i].clear();
                    }
                }
                this.clearCache();
            },
            /**
     * Clear cached links to symbols in other scopes
     */
            clearCache: function() {
                this.cache = {};
            }
        };
        /**
 * @constructor math.expr.Parser
 * Parser contains methods to evaluate or parse expressions, and has a number
 * of convenience methods to get, set, and remove variables from memory. Parser
 * keeps a scope containing variables in memory, which is used for all
 * evaluations.
 *
 * Methods:
 *    var result = parser.eval(expr);    // evaluate an expression
 *    var value = parser.get(name);      // retrieve a variable from the parser
 *    parser.set(name, value);           // set a variable in the parser
 *    parser.remove(name);               // clear a variable from the
 *                                       // parsers scope
 *    parser.clear();                    // clear the parsers scope
 *
 *    // it is possible to parse an expression into a node tree:
 *    var node = parser.parse(expr);     // parse an expression into a node tree
 *    var result = node.eval();          // evaluate a parsed node
 *
 * Example usage:
 *    var parser = new math.expr.Parser();
 *    // Note: there is a convenience method which can be used instead:
 *    // var parser = new math.parser();
 *
 *    // evaluate expressions
 *    parser.eval('sqrt(3^2 + 4^2)');         // 5
 *    parser.eval('sqrt(-4)');                // 2i
 *    parser.eval('2 inch in cm');            // 5.08 cm
 *    parser.eval('cos(45 deg)');             // 0.7071067811865476
 *
 *    // define variables and functions
 *    parser.eval('x = 7 / 2');               // 3.5
 *    parser.eval('x + 3');                   // 6.5
 *    parser.eval('function f(x, y) = x^y');  // f(x, y)
 *    parser.eval('f(2, 3)');                 // 8
 *
 *    // get and set variables and functions
 *    var x = parser.get('x');                // 7
 *    var f = parser.get('f');                // function
 *    var g = f(3, 2);                        // 9
 *    parser.set('h', 500);
 *    var i = parser.eval('h / 2');           // 250
 *    parser.set('hello', function (name) {
 *        return 'hello, ' + name + '!';
 *    });
 *    parser.eval('hello("user")');           // "hello, user!"
 *
 *    // clear defined functions and variables
 *    parser.clear();
 */
        math.expr.Parser = function Parser() {
            if (!(this instanceof math.expr.Parser)) {
                throw new SyntaxError("Parser constructor must be called with the new operator");
            }
            this.scope = new math.expr.Scope();
        };
        /**
 * Parse an expression end return the parsed function node.
 * The node can be evaluated via node.eval()
 * @param {String} expr
 * @return {Node} node
 * @throws {Error}
 */
        math.expr.Parser.prototype.parse = function(expr) {
            return math.parse(expr, this.scope);
        };
        /**
 * Parse and evaluate the given expression
 * @param {String} expr   A string containing an expression, for example "2+3"
 * @return {*} result     The result, or undefined when the expression was empty
 * @throws {Error}
 */
        math.expr.Parser.prototype.eval = function(expr) {
            var node = math.parse(expr, this.scope);
            return node.eval();
        };
        /**
 * Get a variable (a function or variable) by name from the parsers scope.
 * Returns undefined when not found
 * @param {String} name
 * @return {* | undefined} value
 */
        math.expr.Parser.prototype.get = function(name) {
            return this.scope.get(name);
        };
        /**
 * Set a symbol (a function or variable) by name from the parsers scope.
 * @param {String} name
 * @param {* | undefined} value
 */
        math.expr.Parser.prototype.set = function(name, value) {
            this.scope.set(name, value);
        };
        /**
 * Remove a variable from the parsers scope
 * @param {String} name
 */
        math.expr.Parser.prototype.remove = function(name) {
            this.scope.remove(name);
        };
        /**
 * Clear the scope with variables and functions
 */
        math.expr.Parser.prototype.clear = function() {
            this.scope.clear();
        };
        /**
 * @constructor math.expr.Expression
 *
 * An expression is a linked node which can hold a scope and an expression,
 * and calculates symbol dependencies.
 * Expression is used by Workspace.
 *
 * @param {Object} params Object containing parameters:
 *                        {Number} id
 *                        {String} expression   An expression, for example "2+3"
 *                        {Scope} scope
 *                        {math.expr.Expression} nextNode
 *                        {math.expr.Expression} previousNode
 */
        math.expr.Expression = function Expression(params) {
            this.id = params.id;
            this.scope = params.scope;
            this.nextNode = params.nextNode;
            this.previousNode = params.previousNode;
            // TODO: throw error when id or scope is not given
            this.updateSeq = 0;
            this.node = undefined;
            this.symbols = {};
            this.assignments = {};
            this.updates = {};
            this.result = undefined;
            this.setExpr(params.expression);
        };
        /**
 * Set the node's expression
 * @param {String} expression
 */
        math.expr.Expression.prototype.setExpr = function(expression) {
            this.expression = expression || "";
            this.scope.clear();
            // clear assignments and cache
            this._parse();
            this._analyse();
        };
        /**
 * Get the node's expression
 * @return {String} expression
 */
        math.expr.Expression.prototype.getExpr = function() {
            return this.expression;
        };
        /**
 * get the result of the nodes expression
 * @return {*} result
 */
        math.expr.Expression.prototype.getResult = function() {
            // TODO: automatically evaluate when not up to date?
            return this.result;
        };
        /**
 * parse the node's expression
 * @private
 */
        math.expr.Expression.prototype._parse = function() {
            try {
                this.node = math.parse(this.expression, this.scope);
            } catch (err) {
                var value = "Error: " + String(err.message || err);
                this.node = new ConstantNode(value);
            }
        };
        /**
 * Analyse the expressions node tree: find all symbols, assignments, and updates
 * @private
 */
        math.expr.Expression.prototype._analyse = function() {
            var i, len, node;
            if (this.node) {
                // find symbol nodes
                var symbols = this.node.find({
                    type: math.expr.node.SymbolNode
                });
                this.symbols = {};
                for (i = 0, len = symbols.length; i < len; i++) {
                    node = symbols[i];
                    this.symbols[node.name] = node;
                }
                // find symbol assignments
                var assignments = this.node.find({
                    type: math.expr.node.AssignmentNode
                });
                this.assignments = {};
                for (i = 0, len = assignments.length; i < len; i++) {
                    node = assignments[i];
                    this.assignments[node.name] = node;
                }
                // find symbol updates
                var updates = this.node.find({
                    type: math.expr.node.UpdateNode
                });
                this.updates = {};
                for (i = 0, len = updates.length; i < len; i++) {
                    node = updates[i];
                    this.updates[node.name] = node;
                }
            }
        };
        /**
 * Evaluate the node expression
 * @return {*} result
 */
        math.expr.Expression.prototype.eval = function() {
            try {
                this.scope.clear();
                // clear assignments and cache
                this.result = this.node.eval();
            } catch (err) {
                this.scope.clear();
                // clear assignments and cache
                this.result = "Error: " + String(err.message || err);
            }
            return this.result;
        };
        /**
 * @constructor math.expr.Workspace
 *
 * Workspace manages a set of expressions. Expressions can be added,
 * replaced, deleted, and inserted in the workspace. The workspace keeps
 * track on the dependencies between the expressions, and automatically
 * re-evaluates depending expressions when variables or function
 * definitions are changed in the workspace.
 *
 * Methods:
 *     var id = workspace.append(expr);
 *     var id = workspace.insertBefore(expr, beforeId);
 *     var id = workspace.insertAfter(expr, afterId);
 *     workspace.replace(expr, id);
 *     workspace.remove(id);
 *     workspace.clear();
 *     var expr   = workspace.getExpr(id);
 *     var result = workspace.getResult(id);
 *     var deps   = workspace.getDependencies(id);
 *     var changes = workspace.getChanges(updateSeq);
 *
 * Usage:
 *     var workspace = new math.expr.Workspace();
 *     var id0 = workspace.append('a = 3/4');
 *     var id1 = workspace.append('a + 2');
 *     console.log('a + 2 = ' + workspace.getResult(id1));
 *     workspace.replace('a=5/2', id0);
 *     console.log('a + 2 = ' + workspace.getResult(id1));
 */
        function Workspace() {
            this.idMax = -1;
            this.updateSeq = 0;
            this.scope = new math.expr.Scope();
            this.nodes = {};
            this.firstNode = undefined;
            this.lastNode = undefined;
        }
        math.expr.Workspace = Workspace;
        /**
 * clear the workspace
 */
        Workspace.prototype.clear = function() {
            this.nodes = {};
            this.firstNode = undefined;
            this.lastNode = undefined;
        };
        /**
 * append an expression to the workspace
 * @param {String}    expression
 * @return {Number}   id of the created node
 */
        Workspace.prototype.append = function(expression) {
            // create the node
            var id = this._getNewId();
            var parentScope = this.lastNode ? this.lastNode.scope : this.scope;
            var scope = new math.expr.Scope(parentScope);
            var node = new math.expr.Expression({
                id: id,
                expression: expression,
                scope: scope,
                nextNode: undefined,
                previousNode: this.lastNode
            });
            this.nodes[id] = node;
            // link next and previous nodes
            if (!this.firstNode) {
                this.firstNode = node;
            }
            if (this.lastNode) {
                this.lastNode.nextNode = node;
            }
            this.lastNode = node;
            // update this node
            this._update([ id ]);
            return id;
        };
        /**
 * insert an expression before an existing expression
 * @param {String} expression   the new expression
 * @param {Number} beforeId     id of an existing expression
 * @return {Number} id          id of the created node
 */
        Workspace.prototype.insertBefore = function(expression, beforeId) {
            var nextNode = this.nodes[beforeId];
            if (!nextNode) {
                throw new RangeError('Node with id "' + beforeId + '" not found');
            }
            var previousNode = nextNode.previousNode;
            // create the node
            var id = this._getNewId();
            var previousScope = previousNode ? previousNode.scope : this.scope;
            var scope = new math.expr.Scope(previousScope);
            var node = new math.expr.Expression({
                id: id,
                expression: expression,
                scope: scope,
                nextNode: nextNode,
                previousNode: previousNode
            });
            this.nodes[id] = node;
            // link next and previous nodes
            if (previousNode) {
                previousNode.nextNode = node;
            } else {
                this.firstNode = node;
            }
            nextNode.previousNode = node;
            // link to the new the scope
            nextNode.scope.parentScope = node.scope;
            // update this node and all dependent nodes
            var ids = this.getDependencies(id);
            if (ids.indexOf(id) == -1) {
                ids.unshift(id);
            }
            this._update(ids);
            return id;
        };
        /**
 * insert an expression after an existing expression
 * @param {String} expression   the new expression
 * @param {Number} afterId      id of an existing expression
 * @return {Number} id          id of the created expression
 */
        Workspace.prototype.insertAfter = function(expression, afterId) {
            var previousNode = this.nodes[afterId];
            if (!previousNode) {
                throw new RangeError('Node with id "' + afterId + '" not found');
            }
            var nextNode = previousNode.nextNode;
            if (nextNode) {
                return this.insertBefore(expression, nextNode.id);
            } else {
                return this.append(expression);
            }
        };
        /**
 * remove an expression. If the expression is not found, no action will
 * be taken.
 * @param {Number} id           id of an existing expression
 */
        Workspace.prototype.remove = function(id) {
            var node = this.nodes[id];
            if (!node) {
                throw new RangeError('Node with id "' + id + '" not found');
            }
            // get the dependencies (needed to update them after deletion of this node)
            var dependentIds = this.getDependencies(id);
            // adjust links to previous and next nodes
            var previousNode = node.previousNode;
            var nextNode = node.nextNode;
            if (previousNode) {
                previousNode.nextNode = nextNode;
            } else {
                this.firstNode = nextNode;
            }
            if (nextNode) {
                nextNode.previousNode = previousNode;
            } else {
                this.lastNode = previousNode;
            }
            // re-link the scope
            var previousScope = previousNode ? previousNode.scope : this.scope;
            if (nextNode) {
                nextNode.scope.parentScope = previousScope;
            }
            // remove the node
            delete this.nodes[id];
            // update all dependent nodes
            this._update(dependentIds);
        };
        /**
 * replace an existing expression
 * @param {String} expression   the new expression
 * @param {Number} id           id of an existing expression
 */
        Workspace.prototype.replace = function(expression, id) {
            var node = this.nodes[id];
            if (!node) {
                throw new RangeError('Node with id "' + id + '" not found');
            }
            // get the dependencies
            var dependentIds = [ id ];
            Workspace._merge(dependentIds, this.getDependencies(id));
            // replace the expression
            node.setExpr(expression);
            // add the new dependencies
            Workspace._merge(dependentIds, this.getDependencies(id));
            // update all dependencies
            this._update(dependentIds);
        };
        /**
 * Merge array2 into array1, only adding distinct elements.
 * The elements are not sorted.
 * @param {Array} array1
 * @param {Array} array2
 * @private
 */
        Workspace._merge = function(array1, array2) {
            for (var i = 0, iMax = array2.length; i < iMax; i++) {
                var elem = array2[i];
                if (array1.indexOf(elem) == -1) {
                    array1.push(elem);
                }
            }
        };
        /**
 * Retrieve the id's of the nodes which are dependent on this node
 * @param {Number} id
 * @return {Number[]} id's of dependent nodes. The ids are not ordered
 */
        Workspace.prototype.getDependencies = function(id) {
            var node = this.nodes[id], ids = [], names = {}, name;
            if (!node) {
                throw new RangeError('Node with id "' + id + '" not found');
            }
            /**
     * Append all symbol assignments and updates of given Expression to the list
     * with names (value == true), or remove them (value == false).
     * @param {math.expr.Expression} expr
     * @param {boolean} value
     */
            var appendNames = function(expr, value) {
                var assignments = expr.assignments, updates = expr.updates, name;
                for (name in assignments) {
                    if (assignments.hasOwnProperty(name)) {
                        names[name] = value;
                    }
                }
                for (name in updates) {
                    if (updates.hasOwnProperty(name)) {
                        names[name] = value;
                    }
                }
            };
            // append the assignments of the node itself
            appendNames(node, true);
            // loop over all next nodes and test dependency
            node = node.nextNode;
            while (node) {
                var symbols = node.symbols, depends = false;
                // test if any of the nodes symbols are listed in the names map
                for (name in symbols) {
                    if (symbols.hasOwnProperty(name) && names[name] == true) {
                        depends = true;
                        break;
                    }
                }
                if (depends) {
                    // append all assignments done in this node
                    appendNames(node, true);
                    ids.push(node.id);
                } else {
                    // detach all assignments done by this node
                    appendNames(node, false);
                }
                node = node.nextNode;
            }
            return ids;
        };
        /**
 * Retrieve an expression, the original string
 * @param {Number} id    Id of the expression to be retrieved
 * @return {String}      The original expression as a string
 */
        Workspace.prototype.getExpr = function(id) {
            var node = this.nodes[id];
            if (!node) {
                throw new RangeError('Node with id "' + id + '" not found');
            }
            return node.getExpr();
        };
        /**
 * get the result of and expression
 * @param {Number} id
 * @return {*} result
 */
        Workspace.prototype.getResult = function(id) {
            var node = this.nodes[id];
            if (!node) {
                throw new RangeError('Node with id "' + id + '" not found');
            }
            return node.getResult();
        };
        /**
 * Update the results of an expression and all dependent expressions
 * @param {Number[]} ids    Ids of the expressions to be updated
 * @private
 */
        Workspace.prototype._update = function(ids) {
            this.updateSeq++;
            var updateSeq = this.updateSeq;
            var nodes = this.nodes;
            for (var i = 0, iMax = ids.length; i < iMax; i++) {
                var id = ids[i];
                var node = nodes[id];
                if (node) {
                    node.eval();
                    //console.log('eval node=' + id + ' result=' + node.result.toString()); // TODO: cleanup
                    node.updateSeq = updateSeq;
                } else {}
            }
        };
        /**
 * Get all changes since an update sequence
 * @param {Number} updateSeq.    Optional. if not provided, all changes are
 *                               since the creation of the workspace are returned
 * @return {Object} ids    Object containing two parameters:
 *                         param {Number[]} ids         Array containing
 *                                                      the ids of the changed
 *                                                      expressions
 *                         param {Number} updateSeq     the current update
 *                                                      sequence
 */
        Workspace.prototype.getChanges = function(updateSeq) {
            var changedIds = [];
            var node = this.firstNode;
            updateSeq = updateSeq || 0;
            while (node) {
                if (node.updateSeq > updateSeq) {
                    changedIds.push(node.id);
                }
                node = node.nextNode;
            }
            return {
                ids: changedIds,
                updateSeq: this.updateSeq
            };
        };
        /**
 * Return a new, unique id for an expression
 * @return {Number} new id
 * @private
 */
        Workspace.prototype._getNewId = function() {
            this.idMax++;
            return this.idMax;
        };
        /**
 * String representation of the Workspace
 * @return {String} description
 */
        Workspace.prototype.toString = function() {
            return JSON.stringify(this.toJSON());
        };
        /**
 * JSON representation of the Workspace
 * @return {Object} description
 */
        Workspace.prototype.toJSON = function() {
            var json = [];
            var node = this.firstNode;
            while (node) {
                var desc = {
                    id: node.id,
                    expression: node.expression,
                    dependencies: this.getDependencies(node.id)
                };
                try {
                    desc.result = node.getResult();
                } catch (err) {
                    desc.result = "Error: " + String(err.message || err);
                }
                json.push(desc);
                node = node.nextNode;
            }
            return json;
        };
        /**
 * Calculate the absolute value of a value.
 *
 *     abs(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.abs = function abs(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("abs", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.abs(x);
            }
            if (x instanceof Complex) {
                return Math.sqrt(x.re * x.re + x.im * x.im);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.abs);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.abs(x.valueOf());
            }
            throw newUnsupportedTypeError("abs", x);
        };
        /**
 * Add two values
 *
 *     x + y
 *     add(x, y)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Number | Complex | Unit | String | Array | Matrix} res
 */
        math.add = function add(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("add", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    // number + number
                    return x + y;
                } else if (y instanceof Complex) {
                    // number + complex
                    return new Complex(x + y.re, y.im);
                }
            } else if (x instanceof Complex) {
                if (isNumber(y)) {
                    // complex + number
                    return new Complex(x.re + y, x.im);
                } else if (y instanceof Complex) {
                    // complex + complex
                    return new Complex(x.re + y.re, x.im + y.im);
                }
            } else if (x instanceof Unit) {
                if (y instanceof Unit) {
                    if (!x.equalBase(y)) {
                        throw new Error("Units do not match");
                    }
                    if (x.value == null) {
                        throw new Error("Unit on left hand side of operator + has an undefined value");
                    }
                    if (y.value == null) {
                        throw new Error("Unit on right hand side of operator + has an undefined value");
                    }
                    var res = x.clone();
                    res.value += y.value;
                    res.fixPrefix = false;
                    return res;
                }
            }
            if (isString(x) || isString(y)) {
                return x + y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.add);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive value
                return math.add(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("add", x, y);
        };
        /**
 * Round a value towards plus infinity
 *
 *     ceil(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.ceil = function ceil(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("ceil", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.ceil(x);
            }
            if (x instanceof Complex) {
                return new Complex(Math.ceil(x.re), Math.ceil(x.im));
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.ceil);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.ceil(x.valueOf());
            }
            throw newUnsupportedTypeError("ceil", x);
        };
        /**
 * Compute the cube of a value
 *
 *     x .* x .* x
 *     cube(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.cube = function cube(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("cube", arguments.length, 1);
            }
            if (isNumber(x)) {
                return x * x * x;
            }
            if (x instanceof Complex) {
                return math.multiply(math.multiply(x, x), x);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.cube);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.cube(x.valueOf());
            }
            throw newUnsupportedTypeError("cube", x);
        };
        /**
 * Divide two values.
 *
 *     x / y
 *     divide(x, y)
 *
 * @param  {Number | Complex | Unit | Array | Matrix} x
 * @param  {Number | Complex} y
 * @return {Number | Complex | Unit | Array | Matrix} res
 */
        math.divide = function divide(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("divide", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    // number / number
                    return x / y;
                } else if (y instanceof Complex) {
                    // number / complex
                    return _divideComplex(new Complex(x, 0), y);
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    // complex / number
                    return _divideComplex(x, new Complex(y, 0));
                } else if (y instanceof Complex) {
                    // complex / complex
                    return _divideComplex(x, y);
                }
            }
            if (x instanceof Unit) {
                if (isNumber(y)) {
                    var res = x.clone();
                    res.value /= y;
                    return res;
                }
            }
            if (x instanceof Array || x instanceof Matrix) {
                if (y instanceof Array || y instanceof Matrix) {
                    // TODO: implement matrix right division using pseudo inverse
                    // http://www.mathworks.nl/help/matlab/ref/mrdivide.html
                    // http://www.gnu.org/software/octave/doc/interpreter/Arithmetic-Ops.html
                    // http://stackoverflow.com/questions/12263932/how-does-gnu-octave-matrix-division-work-getting-unexpected-behaviour
                    return math.multiply(x, math.inv(y));
                } else {
                    // matrix / scalar
                    return util.map2(x, y, math.divide);
                }
            }
            if (y instanceof Array || y instanceof Matrix) {
                // TODO: implement matrix right division using pseudo inverse
                return math.multiply(x, math.inv(y));
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive value
                return math.divide(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("divide", x, y);
        };
        /**
 * Divide two complex numbers. x / y or divide(x, y)
 * @param {Complex} x
 * @param {Complex} y
 * @return {Complex} res
 * @private
 */
        function _divideComplex(x, y) {
            var den = y.re * y.re + y.im * y.im;
            return new Complex((x.re * y.re + x.im * y.im) / den, (x.im * y.re - x.re * y.im) / den);
        }
        /**
 * Check if value x equals y,
 *
 *     x == y
 *     equal(x, y)
 *
 * For matrices, the function is evaluated element wise.
 * In case of complex numbers, x.re must equal y.re, and x.im must equal y.im.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Boolean | Array | Matrix} res
 */
        math.equal = function equal(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("equal", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x == y;
                } else if (y instanceof Complex) {
                    return x == y.re && y.im == 0;
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return x.re == y && x.im == 0;
                } else if (y instanceof Complex) {
                    return x.re == y.re && x.im == y.im;
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value == y.value;
            }
            if (isString(x) || isString(y)) {
                return x == y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.equal);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return equal(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("equal", x, y);
        };
        /**
 * Calculate the exponent of a value
 *
 *     exp(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.exp = function exp(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("exp", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.exp(x);
            }
            if (x instanceof Complex) {
                var r = Math.exp(x.re);
                return new Complex(r * Math.cos(x.im), r * Math.sin(x.im));
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.exp);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.exp(x.valueOf());
            }
            throw newUnsupportedTypeError("exp", x);
        };
        /**
 * Round a value towards zero
 *
 *     fix(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.fix = function fix(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("fix", arguments.length, 1);
            }
            if (isNumber(x)) {
                return x > 0 ? Math.floor(x) : Math.ceil(x);
            }
            if (x instanceof Complex) {
                return new Complex(x.re > 0 ? Math.floor(x.re) : Math.ceil(x.re), x.im > 0 ? Math.floor(x.im) : Math.ceil(x.im));
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.fix);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.fix(x.valueOf());
            }
            throw newUnsupportedTypeError("fix", x);
        };
        /**
 * Round a value towards minus infinity
 *
 *     floor(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.floor = function floor(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("floor", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.floor(x);
            }
            if (x instanceof Complex) {
                return new Complex(Math.floor(x.re), Math.floor(x.im));
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.floor);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.floor(x.valueOf());
            }
            throw newUnsupportedTypeError("floor", x);
        };
        /**
 * Calculate the greatest common divisor for two or more values or arrays.
 *
 *     gcd(a, b)
 *     gcd(a, b, c, ...)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {... Number | Array | Matrix} args    two or more integer numbers
 * @return {Number | Array | Matrix} greatest common divisor
 */
        math.gcd = function gcd(args) {
            var a = arguments[0], b = arguments[1], t;
            if (arguments.length == 2) {
                // two arguments
                if (isNumber(a) && isNumber(b)) {
                    if (!isInteger(a) || !isInteger(b)) {
                        throw new Error("Parameters in function gcd must be integer numbers");
                    }
                    // http://en.wikipedia.org/wiki/Euclidean_algorithm
                    while (b != 0) {
                        t = b;
                        b = a % t;
                        a = t;
                    }
                    return Math.abs(a);
                }
                // evaluate gcd element wise
                if (a instanceof Array || a instanceof Matrix || b instanceof Array || b instanceof Matrix) {
                    return util.map2(a, b, math.gcd);
                }
                if (a.valueOf() !== a || b.valueOf() !== b) {
                    // fallback on the objects primitive value
                    return math.gcd(a.valueOf(), b.valueOf());
                }
                throw newUnsupportedTypeError("gcd", a, b);
            }
            if (arguments.length > 2) {
                // multiple arguments. Evaluate them iteratively
                for (var i = 1; i < arguments.length; i++) {
                    a = math.gcd(a, arguments[i]);
                }
                return a;
            }
            // zero or one argument
            throw new SyntaxError("Function gcd expects two or more arguments");
        };
        /**
 * Check if value x is larger y
 *
 *    x > y
 *    larger(x, y)
 *
 * For matrices, the function is evaluated element wise.
 * In case of complex numbers, the absolute values of a and b are compared.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Boolean | Array | Matrix} res
 */
        math.larger = function larger(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("larger", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x > y;
                } else if (y instanceof Complex) {
                    return x > math.abs(y);
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return math.abs(x) > y;
                } else if (y instanceof Complex) {
                    return math.abs(x) > math.abs(y);
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value > y.value;
            }
            if (isString(x) || isString(y)) {
                return x > y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.larger);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.larger(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("larger", x, y);
        };
        /**
 * Check if value x is larger or equal to y
 *
 *     x >= y
 *     largereq(x, y)
 *
 * For matrices, the function is evaluated element wise.
 * In case of complex numbers, the absolute values of a and b are compared.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Boolean | Array | Matrix} res
 */
        math.largereq = function largereq(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("largereq", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x >= y;
                } else if (y instanceof Complex) {
                    return x >= math.abs(y);
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return math.abs(x) >= y;
                } else if (y instanceof Complex) {
                    return math.abs(x) >= math.abs(y);
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value >= y.value;
            }
            if (isString(x) || isString(y)) {
                return x >= y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.largereq);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.largereq(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("largereq", x, y);
        };
        /**
 * Calculate the least common multiple for two or more values or arrays.
 *
 *     lcm(a, b)
 *     lcm(a, b, c, ...)
 *
 * lcm is defined as:
 *     lcm(a, b) = abs(a * b) / gcd(a, b)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {... Number | Array | Matrix} args    two or more integer numbers
 * @return {Number | Array | Matrix} least common multiple
 */
        math.lcm = function lcm(args) {
            var a = arguments[0], b = arguments[1], t;
            if (arguments.length == 2) {
                // two arguments
                if (isNumber(a) && isNumber(b)) {
                    if (!isInteger(a) || !isInteger(b)) {
                        throw new Error("Parameters in function lcm must be integer numbers");
                    }
                    // http://en.wikipedia.org/wiki/Euclidean_algorithm
                    // evaluate gcd here inline to reduce overhead
                    var prod = a * b;
                    while (b != 0) {
                        t = b;
                        b = a % t;
                        a = t;
                    }
                    return Math.abs(prod / a);
                }
                // evaluate lcm element wise
                if (a instanceof Array || a instanceof Matrix || b instanceof Array || b instanceof Matrix) {
                    return util.map2(a, b, math.lcm);
                }
                if (a.valueOf() !== a || b.valueOf() !== b) {
                    // fallback on the objects primitive value
                    return math.lcm(a.valueOf(), b.valueOf());
                }
                throw newUnsupportedTypeError("lcm", a, b);
            }
            if (arguments.length > 2) {
                // multiple arguments. Evaluate them iteratively
                for (var i = 1; i < arguments.length; i++) {
                    a = math.lcm(a, arguments[i]);
                }
                return a;
            }
            // zero or one argument
            throw new SyntaxError("Function lcm expects two or more arguments");
        };
        /**
 * Calculate the logarithm of a value
 *
 *     log(x)
 *     log(x, base)
 *
 * base is optional. If not provided, the natural logarithm of x is calculated.
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @param {Number | Complex} [base]
 * @return {Number | Complex | Array | Matrix} res
 */
        math.log = function log(x, base) {
            if (arguments.length == 1) {
                // calculate natural logarithm, log(x)
                if (isNumber(x)) {
                    if (x >= 0) {
                        return Math.log(x);
                    } else {
                        // negative value -> complex value computation
                        return math.log(new Complex(x, 0));
                    }
                }
                if (x instanceof Complex) {
                    return new Complex(Math.log(Math.sqrt(x.re * x.re + x.im * x.im)), Math.atan2(x.im, x.re));
                }
                if (x instanceof Array || x instanceof Matrix) {
                    return util.map(x, math.log);
                }
                if (x.valueOf() !== x) {
                    // fallback on the objects primitive values
                    return math.log(x.valueOf());
                }
                throw newUnsupportedTypeError("log", x);
            } else if (arguments.length == 2) {
                // calculate logarithm for a specified base, log(x, base)
                return math.divide(math.log(x), math.log(base));
            } else {
                throw newArgumentsError("log", arguments.length, 1, 2);
            }
        };
        /**
 * Calculate the 10-base logarithm of a value
 *
 *     log10(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.log10 = function log10(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("log10", arguments.length, 1);
            }
            if (isNumber(x)) {
                if (x >= 0) {
                    return Math.log(x) / Math.LN10;
                } else {
                    // negative value -> complex value computation
                    return math.log10(new Complex(x, 0));
                }
            }
            if (x instanceof Complex) {
                return new Complex(Math.log(Math.sqrt(x.re * x.re + x.im * x.im)) / Math.LN10, Math.atan2(x.im, x.re) / Math.LN10);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.log10);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.log10(x.valueOf());
            }
            throw newUnsupportedTypeError("log10", x);
        };
        /**
 * Calculates the modulus, the remainder of an integer division.
 *
 *     x % y
 *     mod(x, y)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param  {Number | Complex | Array | Matrix} x
 * @param  {Number | Complex | Array | Matrix} y
 * @return {Number | Array | Matrix} res
 */
        math.mod = function mod(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("mod", arguments.length, 2);
            }
            // see http://functions.wolfram.com/IntegerFunctions/Mod/
            if (isNumber(x) && isNumber(y)) {
                // number % number
                return _mod(x, y);
            }
            // TODO: implement mod for complex values
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.mod);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.mod(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("mod", x, y);
        };
        /**
 * Calculate the modulus of two numbers
 * @param {Number} x
 * @param {Number} y
 * @returns {number} res
 * @private
 */
        function _mod(x, y) {
            if (y > 0) {
                if (x > 0) {
                    return x % y;
                } else if (x == 0) {
                    return 0;
                } else {
                    // x < 0
                    return x - y * Math.floor(x / y);
                }
            } else if (y == 0) {
                return x;
            } else {
                // y < 0
                // TODO: implement mod for a negative divisor
                throw new Error("Cannot calculate mod for a negative divisor");
            }
        }
        /**
 * Multiply two values.
 *
 *     x * y
 *     multiply(x, y)
 *
 * @param  {Number | Complex | Unit | Array | Matrix} x
 * @param  {Number | Complex | Unit | Array | Matrix} y
 * @return {Number | Complex | Unit | Array | Matrix} res
 */
        math.multiply = function multiply(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("multiply", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    // number * number
                    return x * y;
                } else if (y instanceof Complex) {
                    // number * complex
                    return _multiplyComplex(new Complex(x, 0), y);
                } else if (y instanceof Unit) {
                    res = y.clone();
                    res.value *= x;
                    return res;
                }
            } else if (x instanceof Complex) {
                if (isNumber(y)) {
                    // complex * number
                    return _multiplyComplex(x, new Complex(y, 0));
                } else if (y instanceof Complex) {
                    // complex * complex
                    return _multiplyComplex(x, y);
                }
            } else if (x instanceof Unit) {
                if (isNumber(y)) {
                    res = x.clone();
                    res.value *= y;
                    return res;
                }
            } else if (x instanceof Array) {
                if (y instanceof Array) {
                    // matrix * matrix
                    var sizeX = util.size(x);
                    var sizeY = util.size(y);
                    if (sizeX.length != 2) {
                        throw new Error("Can only multiply a 2 dimensional matrix " + "(A has " + sizeX.length + " dimensions)");
                    }
                    if (sizeY.length != 2) {
                        throw new Error("Can only multiply a 2 dimensional matrix " + "(B has " + sizeY.length + " dimensions)");
                    }
                    if (sizeX[1] != sizeY[0]) {
                        throw new RangeError("Dimensions mismatch in multiplication. " + "Columns of A must match rows of B " + "(A is " + sizeX[0] + "x" + sizeX[1] + ", B is " + sizeY[0] + "x" + sizeY[1] + ", " + sizeY[1] + " != " + sizeY[0] + ")");
                    }
                    // TODO: performance of matrix multiplication can be improved
                    var res = [], rows = sizeX[0], cols = sizeY[1], num = sizeX[1], multiply = math.multiply, add = math.add;
                    for (var r = 0; r < rows; r++) {
                        res[r] = [];
                        for (var c = 0; c < cols; c++) {
                            var result = null;
                            for (var n = 0; n < num; n++) {
                                var p = multiply(x[r][n], y[n][c]);
                                result = result == null ? p : add(result, p);
                            }
                            res[r][c] = result;
                        }
                    }
                    return res;
                } else if (y instanceof Matrix) {
                    return new Matrix(math.multiply(x.valueOf(), y.valueOf()));
                } else {
                    // matrix * scalar
                    return util.map2(x, y, math.multiply);
                }
            } else if (x instanceof Matrix) {
                return new Matrix(math.multiply(x.valueOf(), y.valueOf()));
            }
            if (y instanceof Array) {
                // scalar * matrix
                return util.map2(x, y, math.multiply);
            } else if (y instanceof Matrix) {
                return new Matrix(math.multiply(x.valueOf(), y.valueOf()));
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.multiply(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("multiply", x, y);
        };
        /**
 * Multiply two complex numbers. x * y or multiply(x, y)
 * @param {Complex} x
 * @param {Complex} y
 * @return {Complex} res
 * @private
 */
        function _multiplyComplex(x, y) {
            return new Complex(x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re);
        }
        /**
 * Calculates the power of x to y
 *
 *     x ^ y
 *     pow(x, y)
 *
 * @param  {Number | Complex | Array | Matrix} x
 * @param  {Number | Complex} y
 * @return {Number | Complex | Array | Matrix} res
 */
        math.pow = function pow(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("pow", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    if (isInteger(y) || x >= 0) {
                        // real value computation
                        return Math.pow(x, y);
                    } else {
                        return powComplex(new Complex(x, 0), new Complex(y, 0));
                    }
                } else if (y instanceof Complex) {
                    return powComplex(new Complex(x, 0), y);
                }
            } else if (x instanceof Complex) {
                if (isNumber(y)) {
                    return powComplex(x, new Complex(y, 0));
                } else if (y instanceof Complex) {
                    return powComplex(x, y);
                }
            } else if (x instanceof Array) {
                if (!isNumber(y) || !isInteger(y) || y < 0) {
                    throw new TypeError("For A^b, b must be a positive integer " + "(value is " + y + ")");
                }
                // verify that A is a 2 dimensional square matrix
                var s = util.size(x);
                if (s.length != 2) {
                    throw new Error("For A^b, A must be 2 dimensional " + "(A has " + s.length + " dimensions)");
                }
                if (s[0] != s[1]) {
                    throw new Error("For A^b, A must be square " + "(size is " + s[0] + "x" + s[1] + ")");
                }
                if (y == 0) {
                    // return the identity matrix
                    return math.eye(s[0]);
                } else {
                    // value > 0
                    var res = x;
                    for (var i = 1; i < y; i++) {
                        res = math.multiply(x, res);
                    }
                    return res;
                }
            } else if (x instanceof Matrix) {
                return new Matrix(math.pow(x.valueOf(), y));
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.pow(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("pow", x, y);
        };
        /**
 * Calculates the power of x to y, x^y, for two complex numbers.
 * @param {Complex} x
 * @param {Complex} y
 * @return {Complex} res
 * @private
 */
        function powComplex(x, y) {
            // complex computation
            // x^y = exp(log(x)*y) = exp((abs(x)+i*arg(x))*y)
            var temp1 = math.log(x);
            var temp2 = math.multiply(temp1, y);
            return math.exp(temp2);
        }
        /**
 * Round a value towards the nearest integer
 *
 *     round(x)
 *     round(x, n)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @param {Number | Array} [n] number of decimals (by default n=0)
 * @return {Number | Complex | Array | Matrix} res
 */
        math.round = function round(x, n) {
            if (arguments.length != 1 && arguments.length != 2) {
                throw newArgumentsError("round", arguments.length, 1, 2);
            }
            if (n == undefined) {
                // round (x)
                if (isNumber(x)) {
                    return Math.round(x);
                }
                if (x instanceof Complex) {
                    return new Complex(Math.round(x.re), Math.round(x.im));
                }
                if (x instanceof Array || x instanceof Matrix) {
                    return util.map(x, math.round);
                }
                if (x.valueOf() !== x) {
                    // fallback on the objects primitive value
                    return math.round(x.valueOf());
                }
                throw newUnsupportedTypeError("round", x);
            } else {
                // round (x, n)
                if (!isNumber(n)) {
                    throw new TypeError("Number of decimals in function round must be an integer");
                }
                if (n !== Math.round(n)) {
                    throw new TypeError("Number of decimals in function round must be integer");
                }
                if (n < 0 || n > 9) {
                    throw new Error("Number of decimals in function round must be in te range of 0-9");
                }
                if (isNumber(x)) {
                    return roundNumber(x, n);
                }
                if (x instanceof Complex) {
                    return new Complex(roundNumber(x.re, n), roundNumber(x.im, n));
                }
                if (x instanceof Array || x instanceof Matrix || n instanceof Array || n instanceof Matrix) {
                    return util.map2(x, n, math.round);
                }
                if (x.valueOf() !== x || n.valueOf() !== n) {
                    // fallback on the objects primitive values
                    return math.round(x.valueOf(), n.valueOf());
                }
                throw newUnsupportedTypeError("round", x, n);
            }
        };
        /**
 * round a number to the given number of decimals, or to zero if decimals is
 * not provided
 * @param {Number} value
 * @param {Number} [decimals]  number of decimals, between 0 and 15 (0 by default)
 * @return {Number} roundedValue
 */
        function roundNumber(value, decimals) {
            if (decimals) {
                var p = Math.pow(10, decimals);
                return Math.round(value * p) / p;
            } else {
                return Math.round(value);
            }
        }
        /**
 * Compute the sign of a value.
 *
 *     sign(x)
 *
 * The sign of a value x is 1 when x > 1, -1 when x < 0, and 0 when x == 0
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.sign = function sign(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("sign", arguments.length, 1);
            }
            if (isNumber(x)) {
                var sign;
                if (x > 0) {
                    sign = 1;
                } else if (x < 0) {
                    sign = -1;
                } else {
                    sign = 0;
                }
                return sign;
            }
            if (x instanceof Complex) {
                var abs = Math.sqrt(x.re * x.re + x.im * x.im);
                return new Complex(x.re / abs, x.im / abs);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.sign);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.sign(x.valueOf());
            }
            throw newUnsupportedTypeError("sign", x);
        };
        /**
 * Check if value x is smaller y
 *
 *     x < y
 *     smaller(x, y)
 *
 * For matrices, the function is evaluated element wise.
 * In case of complex numbers, the absolute values of a and b are compared.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Boolean | Array | Matrix} res
 */
        math.smaller = function smaller(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("smaller", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x < y;
                } else if (y instanceof Complex) {
                    return x < math.abs(y);
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return math.abs(x) < y;
                } else if (y instanceof Complex) {
                    return math.abs(x) < math.abs(y);
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value < y.value;
            }
            if (isString(x) || isString(y)) {
                return x < y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.smaller);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.smaller(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("smaller", x, y);
        };
        /**
 * Check if value a is smaller or equal to b
 *
 *     a <= b
 *     smallereq(a, b)
 *
 * For matrices, the function is evaluated element wise.
 * In case of complex numbers, the absolute values of a and b are compared.
 *
 * @param  {Number | Complex | Unit | String | Array | Matrix} x
 * @param  {Number | Complex | Unit | String | Array | Matrix} y
 * @return {Boolean | Array | Matrix} res
 */
        math.smallereq = function smallereq(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("smallereq", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x <= y;
                } else if (y instanceof Complex) {
                    return x <= math.abs(y);
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return math.abs(x) <= y;
                } else if (y instanceof Complex) {
                    return math.abs(x) <= math.abs(y);
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value <= y.value;
            }
            if (isString(x) || isString(y)) {
                return x <= y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.smallereq);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.smallereq(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("smallereq", x, y);
        };
        /**
 * Calculate the square root of a value
 *
 *     sqrt(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.sqrt = function sqrt(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("sqrt", arguments.length, 1);
            }
            if (isNumber(x)) {
                if (x >= 0) {
                    return Math.sqrt(x);
                } else {
                    return math.sqrt(new Complex(x, 0));
                }
            }
            if (x instanceof Complex) {
                var r = Math.sqrt(x.re * x.re + x.im * x.im);
                if (x.im >= 0) {
                    return new Complex(.5 * Math.sqrt(2 * (r + x.re)), .5 * Math.sqrt(2 * (r - x.re)));
                } else {
                    return new Complex(.5 * Math.sqrt(2 * (r + x.re)), -.5 * Math.sqrt(2 * (r - x.re)));
                }
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.sqrt);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.sqrt(x.valueOf());
            }
            throw newUnsupportedTypeError("sqrt", x);
        };
        /**
 * Compute the square of a value
 *
 *     x .* x
 *     square(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.square = function square(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("square", arguments.length, 1);
            }
            if (isNumber(x)) {
                return x * x;
            }
            if (x instanceof Complex) {
                return math.multiply(x, x);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.square);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.square(x.valueOf());
            }
            throw newUnsupportedTypeError("square", x);
        };
        /**
 * Subtract two values
 *
 *     x - y
 *     subtract(x, y)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param  {Number | Complex | Unit | Array | Matrix} x
 * @param  {Number | Complex | Unit | Array | Matrix} y
 * @return {Number | Complex | Unit | Array | Matrix} res
 */
        math.subtract = function subtract(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("subtract", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    // number - number
                    return x - y;
                } else if (y instanceof Complex) {
                    // number - complex
                    return new Complex(x - y.re, -y.im);
                }
            } else if (x instanceof Complex) {
                if (isNumber(y)) {
                    // complex - number
                    return new Complex(x.re - y, x.im);
                } else if (y instanceof Complex) {
                    // complex - complex
                    return new Complex(x.re - y.re, x.im - y.im);
                }
            } else if (x instanceof Unit) {
                if (y instanceof Unit) {
                    if (!x.equalBase(y)) {
                        throw new Error("Units do not match");
                    }
                    if (x.value == null) {
                        throw new Error("Unit on left hand side of operator - has an undefined value");
                    }
                    if (y.value == null) {
                        throw new Error("Unit on right hand side of operator - has an undefined value");
                    }
                    var res = x.clone();
                    res.value -= y.value;
                    res.fixPrefix = false;
                    return res;
                }
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.subtract);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.subtract(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("subtract", x, y);
        };
        /**
 * Inverse the sign of a value.
 *
 *     -x
 *     unaryminus(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param  {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Unit | Array | Matrix} res
 */
        math.unaryminus = function unaryminus(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("unaryminus", arguments.length, 1);
            }
            if (isNumber(x)) {
                return -x;
            } else if (x instanceof Complex) {
                return new Complex(-x.re, -x.im);
            } else if (x instanceof Unit) {
                var res = x.clone();
                res.value = -x.value;
                return res;
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.unaryminus);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.unaryminus(x.valueOf());
            }
            throw newUnsupportedTypeError("unaryminus", x);
        };
        /**
 * Check if value x unequals y, x != y
 * In case of complex numbers, x.re must unequal y.re, or x.im must unequal y.im
 * @param  {Number | Complex | Unit | String | Array | Matrix | Range} x
 * @param  {Number | Complex | Unit | String | Array | Matrix | Range} y
 * @return {Boolean | Array | Matrix} res
 */
        math.unequal = function unequal(x, y) {
            if (arguments.length != 2) {
                throw newArgumentsError("unequal", arguments.length, 2);
            }
            if (isNumber(x)) {
                if (isNumber(y)) {
                    return x != y;
                } else if (y instanceof Complex) {
                    return x != y.re || y.im != 0;
                }
            }
            if (x instanceof Complex) {
                if (isNumber(y)) {
                    return x.re != y || x.im != 0;
                } else if (y instanceof Complex) {
                    return x.re != y.re || x.im != y.im;
                }
            }
            if (x instanceof Unit && y instanceof Unit) {
                if (!x.equalBase(y)) {
                    throw new Error("Cannot compare units with different base");
                }
                return x.value != y.value;
            }
            if (isString(x) || isString(y)) {
                return x != y;
            }
            if (x instanceof Array || x instanceof Matrix || y instanceof Array || y instanceof Matrix) {
                return util.map2(x, y, math.unequal);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.unequal(x.valueOf(), y.valueOf());
            }
            throw newUnsupportedTypeError("unequal", x, y);
        };
        /**
 * Calculate the extended greatest common divisor for two values.
 *
 *     xgcd(a, b)
 *
 * @param {Number} a       An integer number
 * @param {Number} b       An integer number
 * @return {Array}         An array containing 3 integers [div, m, n]
 *                         where div = gcd(a, b) and a*m + b*n = div
 *
 * @see http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
 */
        math.xgcd = function xgcd(a, b) {
            if (arguments.length == 2) {
                // two arguments
                if (isNumber(a) && isNumber(b)) {
                    if (!isInteger(a) || !isInteger(b)) {
                        throw new Error("Parameters in function xgcd must be integer numbers");
                    }
                    if (b == 0) {
                        return [ a, 1, 0 ];
                    }
                    var tmp = xgcd(b, a % b), div = tmp[0], x = tmp[1], y = tmp[2];
                    return [ div, y, x - y * Math.floor(a / b) ];
                }
                throw newUnsupportedTypeError("xgcd", a, b);
            }
            // zero or one argument
            throw new SyntaxError("Function xgcd expects two arguments");
        };
        /**
 * Compute the argument of a complex value.
 * If x = a + bi, the argument is computed as atan2(b, a).
 *
 *     arg(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Array | Matrix} res
 */
        math.arg = function arg(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("arg", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.atan2(0, x);
            }
            if (x instanceof Complex) {
                return Math.atan2(x.im, x.re);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.arg);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.arg(x.valueOf());
            }
            // handle other types just as non-complex values
            return math.atan2(0, x);
        };
        /**
 * Compute the complex conjugate of a complex value.
 * If x = a+bi, the complex conjugate is a-bi.
 *
 *     conj(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.conj = function conj(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("conj", arguments.length, 1);
            }
            if (isNumber(x)) {
                return x;
            }
            if (x instanceof Complex) {
                return new Complex(x.re, -x.im);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.conj);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.conj(x.valueOf());
            }
            // return a clone of the value for non-complex values
            return clone(x);
        };
        /**
 * Get the imaginary part of a complex number.
 *
 *     im(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Array | Matrix} im
 */
        math.im = function im(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("im", arguments.length, 1);
            }
            if (isNumber(x)) {
                return 0;
            }
            if (x instanceof Complex) {
                return x.im;
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.im);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.im(x.valueOf());
            }
            // return 0 for all non-complex values
            return 0;
        };
        /**
 * Get the real part of a complex number.
 *
 *     re(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Array | Matrix} re
 */
        math.re = function re(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("re", arguments.length, 1);
            }
            if (isNumber(x)) {
                return x;
            }
            if (x instanceof Complex) {
                return x.re;
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.re);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.re(x.valueOf());
            }
            // return a clone of the value itself for all non-complex values
            return math.clone(x);
        };
        /**
 * Create a complex value. Depending on the passed arguments, the function
 * will create and return a new math.type.Complex object.
 *
 * The method accepts the following arguments:
 *     complex()                           creates a complex value with zero
 *                                         as real and imaginary part.
 *     complex(re : number, im : string)   creates a complex value with provided
 *                                         values for real and imaginary part.
 *     complex(arg : string)               parses a string into a complex value.
 *
 * Example usage:
 *     var a = math.complex(3, -4);     // 3 - 4i
 *     a.re = 5;                        // a = 5 - 4i
 *     var i = a.im;                    // -4;
 *     var b = math.complex('2 + 6i');  // 2 + 6i
 *     var c = math.complex();          // 0 + 0i
 *     var d = math.add(a, b);          // 5 + 2i
 *
 * @param {*} [args]
 * @return {Complex} value
 */
        math.complex = function complex(args) {
            switch (arguments.length) {
              case 0:
                // no parameters. Set re and im zero
                return new Complex(0, 0);
                break;

              case 1:
                // parse string into a complex number
                var arg = arguments[0];
                if (arg instanceof Complex) {
                    // create a clone
                    return arg.clone();
                } else if (isString(arg)) {
                    var c = Complex.parse(arg);
                    if (c) {
                        return c;
                    } else {
                        throw new SyntaxError('String "' + arg + '" is no valid complex number');
                    }
                } else {
                    throw new TypeError("Two numbers or a single string expected in function complex");
                }
                break;

              case 2:
                // re and im provided
                return new Complex(arguments[0], arguments[1]);
                break;

              default:
                throw newArgumentsError("complex", arguments.length, 0, 2);
            }
        };
        /**
 * Create a matrix. The function creates a new math.type.Matrix object.
 *
 * The method accepts the following arguments:
 *     matrix()       creates an empty matrix
 *     matrix(data)   creates a matrix with initial data.
 *
 * Example usage:
 *     var m = matrix([[1, 2], [3, 4]);
 *     m.size();                        // [2, 2]
 *     m.resize([3, 2], 5);
 *     m.valueOf();                     // [[1, 2], [3, 4], [5, 5]]
 *     m.get([1, 0])                    // 3
 *
 * @param {Array | Matrix} [data]    A multi dimensional array
 * @return {Matrix} matrix
 */
        math.matrix = function matrix(data) {
            if (arguments.length > 1) {
                throw newArgumentsError("matrix", arguments.length, 0, 1);
            }
            return new Matrix(data);
        };
        /**
 * Create a number or convert a string to a number
 * @param {String | Number | Boolean} [value]
 * @return {Number} num
 */
        math.number = function(value) {
            switch (arguments.length) {
              case 0:
                return 0;

              case 1:
                var num = Number(value);
                if (isNaN(num)) {
                    num = Number(value.valueOf());
                }
                if (isNaN(num)) {
                    throw new SyntaxError(value.toString() + " is no valid number");
                }
                return num;

              default:
                throw newArgumentsError("number", arguments.length, 0, 1);
            }
        };
        /**
 * Create a parser. The function creates a new math.expr.Parser object.
 *
 *    parser()
 *
 * Example usage:
 *     var parser = new math.parser();
 *
 *     // evaluate expressions
 *     var a = parser.eval('sqrt(3^2 + 4^2)'); // 5
 *     var b = parser.eval('sqrt(-4)');        // 2i
 *     var c = parser.eval('2 inch in cm');    // 5.08 cm
 *     var d = parser.eval('cos(45 deg)');     // 0.7071067811865476
 *
 *     // define variables and functions
 *     parser.eval('x = 7 / 2');               // 3.5
 *     parser.eval('x + 3');                   // 6.5
 *     parser.eval('function f(x, y) = x^y');  // f(x, y)
 *     parser.eval('f(2, 3)');                 // 8
 *
 *     // get and set variables and functions
 *     var x = parser.get('x');                // 7
 *     var f = parser.get('f');                // function
 *     var g = f(3, 2);                        // 9
 *     parser.set('h', 500);
 *     var i = parser.eval('h / 2');           // 250
 *     parser.set('hello', function (name) {
 *         return 'hello, ' + name + '!';
 *     });
 *     parser.eval('hello("user")');           // "hello, user!"
 *
 *     // clear defined functions and variables
 *     parser.clear();
 *
 * @return {math.expr.Parser} Parser
 */
        math.parser = function parser() {
            return new math.expr.Parser();
        };
        /**
 * Create a range. The function creates a new math.type.Range object.
 *
 * A range works similar to an Array, with functions like
 * forEach and map. However, a Range object is very cheap to create compared to
 * a large Array with indexes, as it stores only a start, step and end value of
 * the range.
 *
 * The method accepts the following arguments
 *     range(str)                   Create a range from a string, where the
 *                                  string contains the start, optional step,
 *                                  and end, separated by a colon.
 *     range(start, end)            Create a range with start and end and a
 *                                  default step size of 1
 *     range(start, step, end)      Create a range with start, step, and end.
 *
 * Example usage:
 *     var c = math.range(2, 1, 5);     // 2:1:5
 *     c.toArray();                     // [2, 3, 4, 5]
 *     var d = math.range(2, -1, -2);   // 2:-1:-2
 *     d.forEach(function (value, index) {
 *         console.log(index, value);
 *     });
 *     var e = math.range('2:1:5');     // 2:1:5
 *
 * @param {...*} args
 * @return {Range} range
 */
        math.range = function range(args) {
            switch (arguments.length) {
              case 1:
                // parse string into a range
                if (args instanceof Range) {
                    // create a clone
                    return args.clone();
                } else if (isString(args)) {
                    var r = Range.parse(args);
                    if (r) {
                        return r;
                    } else {
                        throw new SyntaxError('String "' + r + '" is no valid range');
                    }
                } else {
                    throw new TypeError("Two or three numbers or a single string expected in function range");
                }
                break;

              case 2:
                // range(start, end)
                return new Range(arguments[0], null, arguments[1]);
                break;

              case 3:
                // range(start, step, end)
                return new Range(arguments[0], arguments[1], arguments[2]);
                break;

              default:
                throw newArgumentsError("range", arguments.length, 2, 3);
            }
        };
        /**
 * Create a string or convert any object into a string
 * @param {*} [value]
 * @return {String} str
 */
        math.string = function(value) {
            switch (arguments.length) {
              case 0:
                return "";

              case 1:
                return _toString(value);

              default:
                throw newArgumentsError("string", arguments.length, 0, 1);
            }
        };
        /**
 * Recursive toString function
 * @param {*} value  Value can be anything: number, string, array, Matrix, ...
 * @returns {String} str
 * @private
 */
        function _toString(value) {
            if (value instanceof Array || value instanceof Matrix || value instanceof Range) {
                var array = value.valueOf();
                var str = "[";
                var len = array.length;
                for (var i = 0; i < len; i++) {
                    if (i != 0) {
                        str += ", ";
                    }
                    str += _toString(array[i]);
                }
                str += "]";
                return str;
            } else if (isNumber(value)) {
                return util.formatNumber(value);
            } else {
                return value.toString();
            }
        }
        /**
 * Create a unit. Depending on the passed arguments, the function
 * will create and return a new math.type.Unit object.
 *
 * The method accepts the following arguments:
 *     unit(unit : string)
 *     unit(value : number, unit : string
 *
 * Example usage:
 *     var a = math.unit(5, 'cm');          // 50 mm
 *     var b = math.unit('23 kg');          // 23 kg
 *     var c = math.in(a, math.unit('m');   // 0.05 m
 *
 * @param {*} args
 * @return {Unit} value
 */
        math.unit = function unit(args) {
            switch (arguments.length) {
              case 1:
                // parse a string
                var arg = arguments[0];
                if (arg instanceof Unit) {
                    // create a clone of the unit
                    return arg.clone();
                } else if (isString(arg)) {
                    if (Unit.isPlainUnit(arg)) {
                        return new Unit(null, arg);
                    }
                    var u = Unit.parse(arg);
                    // a unit with value, like '5cm'
                    if (u) {
                        return u;
                    }
                    throw new SyntaxError('String "' + arg + '" is no valid unit');
                } else {
                    throw new TypeError("A string or a number and string expected in function unit");
                }
                break;

              case 2:
                // a number and a unit
                return new Unit(arguments[0], arguments[1]);
                break;

              default:
                throw newArgumentsError("unit", arguments.length, 1, 2);
            }
        };
        /**
 * Create a workspace. The function creates a new math.expr.Workspace object.
 *
 *     workspace()
 *
 * Workspace manages a set of expressions. Expressions can be added, replace,
 * deleted, and inserted in the workspace. The workspace keeps track on the
 * dependencies between the expressions, and automatically updates results of
 * depending expressions when variables or function definitions are changed in
 * the workspace.
 *
 * Methods:
 *     var id = workspace.append(expr);
 *     var id = workspace.insertBefore(expr, beforeId);
 *     var id = workspace.insertAfter(expr, afterId);
 *     workspace.replace(expr, id);
 *     workspace.remove(id);
 *     workspace.clear();
 *     var expr   = workspace.getExpr(id);
 *     var result = workspace.getResult(id);
 *     var deps   = workspace.getDependencies(id);
 *     var changes = workspace.getChanges(updateSeq);
 *
 * Usage:
 *     var workspace = new math.workspace();
 *     var id0 = workspace.append('a = 3/4');
 *     var id1 = workspace.append('a + 2');
 *     console.log('a + 2 = ' + workspace.getResult(id1));
 *     workspace.replace('a=5/2', id0);
 *     console.log('a + 2 = ' + workspace.getResult(id1));
 *
 * @return {math.expr.Workspace} Workspace
 */
        math.workspace = function workspace() {
            return new math.expr.Workspace();
        };
        /**
 * Concatenate two or more matrices
 * Usage:
 *     math.concat(A, B, C, ...)
 *     math.concat(A, B, C, ..., dim)
 *
 * Where the optional dim is the one-based number of the dimension to be
 * concatenated.
 *
 * @param {... Array | Matrix} args
 * @return {Array | Matrix} res
 */
        math.concat = function concat(args) {
            var i, len = arguments.length, dim = -1, // one-based dimension
            prevDim, asMatrix = false, matrices = [];
            // contains multi dimensional arrays
            for (i = 0; i < len; i++) {
                var arg = arguments[i];
                // test whether we need to return a Matrix (if not we return an Array)
                if (arg instanceof Matrix) {
                    asMatrix = true;
                }
                if (i == len - 1 && isNumber(arg)) {
                    // last argument contains the dimension on which to concatenate
                    prevDim = dim;
                    dim = arg;
                    if (!isInteger(dim) || dim < 1) {
                        throw new TypeError("Dimension number must be a positive integer " + "(dim = " + dim + ")");
                    }
                    if (i > 0 && dim > prevDim) {
                        throw new RangeError("Dimension out of range " + "(" + dim + " > " + prevDim + ")");
                    }
                } else if (arg instanceof Array || arg instanceof Matrix) {
                    // this is a matrix or array
                    var matrix = math.clone(arg.valueOf());
                    var size = math.size(arg);
                    matrices[i] = matrix;
                    prevDim = dim;
                    dim = size.length;
                    // verify whether each of the matrices has the same number of dimensions
                    if (i > 0 && dim != prevDim) {
                        throw new RangeError("Dimension mismatch " + "(" + prevDim + " != " + dim + ")");
                    }
                } else {
                    throw newUnsupportedTypeError("concat", arg);
                }
            }
            if (matrices.length == 0) {
                throw new SyntaxError("At least one matrix expected");
            }
            var res = matrices.shift();
            while (matrices.length) {
                res = _concat(res, matrices.shift(), dim - 1, 0);
            }
            return asMatrix ? new Matrix(res) : res;
        };
        /**
 * Recursively concatenate two matrices.
 * The contents of the matrices is not cloned.
 * @param {Array} a             Multi dimensional array
 * @param {Array} b             Multi dimensional array
 * @param {Number} concatDim    The dimension on which to concatenate (zero-based)
 * @param {Number} dim          The current dim (zero-based)
 * @return {Array} c            The concatenated matrix
 * @private
 */
        function _concat(a, b, concatDim, dim) {
            if (dim < concatDim) {
                // recurse into next dimension
                if (a.length != b.length) {
                    throw new Error("Dimensions mismatch (" + a.length + " != " + b.length + ")");
                }
                var c = [];
                for (var i = 0; i < a.length; i++) {
                    c[i] = _concat(a[i], b[i], concatDim, dim + 1);
                }
                return c;
            } else {
                // concatenate this dimension
                return a.concat(b);
            }
        }
        /**
 * @constructor det
 * Calculate the determinant of a matrix
 *
 *     det(x)
 *
 * @param {Array | Matrix} x
 * @return {Number} determinant
 */
        math.det = function det(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("det", arguments.length, 1);
            }
            var size = math.size(x);
            switch (size.length) {
              case 0:
                // scalar
                return math.clone(x);
                break;

              case 1:
                // vector
                if (size[0] == 1) {
                    return math.clone(x.valueOf()[0]);
                } else {
                    throw new RangeError("Matrix must be square " + "(size: " + math.format(size) + ")");
                }
                break;

              case 2:
                // two dimensional array
                var rows = size[0];
                var cols = size[1];
                if (rows == cols) {
                    return _det(x.valueOf(), rows, cols);
                } else {
                    throw new RangeError("Matrix must be square " + "(size: " + math.format(size) + ")");
                }
                break;

              default:
                // multi dimensional array
                throw new RangeError("Matrix must be two dimensional " + "(size: " + math.format(size) + ")");
            }
        };
        /**
 * Calculate the determinant of a matrix
 * @param {Array[]} matrix  A square, two dimensional matrix
 * @param {Number} rows     Number of rows of the matrix (zero-based)
 * @param {Number} cols     Number of columns of the matrix (zero-based)
 * @returns {Number} det
 * @private
 */
        function _det(matrix, rows, cols) {
            var multiply = math.multiply, subtract = math.subtract;
            if (rows == 1) {
                // this is a 1 x 1 matrix
                return matrix[0][0];
            } else if (rows == 2) {
                // this is a 2 x 2 matrix
                // the determinant of [a11,a12;a21,a22] is det = a11*a22-a21*a12
                return subtract(multiply(matrix[0][0], matrix[1][1]), multiply(matrix[1][0], matrix[0][1]));
            } else {
                // this is an n x n matrix
                var det = 1;
                var lead = 0;
                for (var r = 0; r < rows; r++) {
                    if (lead >= cols) {
                        break;
                    }
                    var i = r;
                    // Find the pivot element.
                    while (matrix[i][lead] == 0) {
                        i++;
                        if (i == rows) {
                            i = r;
                            lead++;
                            if (lead == cols) {
                                // We found the last pivot.
                                if (util.deepEqual(matrix, math.eye(rows).valueOf())) {
                                    return math.round(det, 6);
                                } else {
                                    return 0;
                                }
                            }
                        }
                    }
                    if (i != r) {
                        // Swap rows i and r, which negates the determinant.
                        for (var a = 0; a < cols; a++) {
                            var temp = matrix[i][a];
                            matrix[i][a] = matrix[r][a];
                            matrix[r][a] = temp;
                        }
                        det *= -1;
                    }
                    // Scale row r and the determinant simultaneously.
                    var div = matrix[r][lead];
                    for (var a = 0; a < cols; a++) {
                        matrix[r][a] = matrix[r][a] / div;
                    }
                    det *= div;
                    // Back-substitute upwards.
                    for (var j = 0; j < rows; j++) {
                        if (j != r) {
                            // Taking linear combinations does not change the det.
                            var c = matrix[j][lead];
                            for (var a = 0; a < cols; a++) {
                                matrix[j][a] = matrix[j][a] - matrix[r][a] * c;
                            }
                        }
                    }
                    lead++;
                }
                // If reduction did not result in the identity, the matrix is singular.
                if (util.deepEqual(matrix, math.eye(rows).valueOf())) {
                    return math.round(det, 6);
                } else {
                    return 0;
                }
            }
        }
        /**
 * Create a diagonal matrix or retrieve the diagonal of a matrix
 *
 *     diag(v)
 *     diag(v, k)
 *     diag(X)
 *     diag(X, k)
 *
 * TODO: more documentation on diag
 *
 * @param {Number | Matrix | Array} x
 * @param {Number} [k]
 * @return {Matrix} matrix
 */
        math.diag = function diag(x, k) {
            var data, vector, i, iMax;
            if (arguments.length != 1 && arguments.length != 2) {
                throw newArgumentsError("diag", arguments.length, 1, 2);
            }
            if (k) {
                if (!isNumber(k) || !isInteger(k)) {
                    throw new TypeError("Second parameter in function diag must be an integer");
                }
            } else {
                k = 0;
            }
            var kSuper = k > 0 ? k : 0;
            var kSub = k < 0 ? -k : 0;
            // convert to matrix
            if (!(x instanceof Matrix) && !(x instanceof Range)) {
                x = new Matrix(x);
            }
            // get as array when the matrix is a vector
            var s;
            if (x.isVector()) {
                x = x.toVector();
                s = [ x.length ];
            } else {
                s = x.size();
            }
            switch (s.length) {
              case 1:
                // x is a vector. create diagonal matrix
                vector = x.valueOf();
                var matrix = new Matrix();
                matrix.resize([ vector.length + kSub, vector.length + kSuper ]);
                data = matrix.valueOf();
                iMax = vector.length;
                for (i = 0; i < iMax; i++) {
                    data[i + kSub][i + kSuper] = math.clone(vector[i]);
                }
                return matrix;
                break;

              case 2:
                // x is a matrix get diagonal from matrix
                vector = [];
                data = x.valueOf();
                iMax = Math.min(s[0] - kSub, s[1] - kSuper);
                for (i = 0; i < iMax; i++) {
                    vector[i] = math.clone(data[i + kSub][i + kSuper]);
                }
                return new Matrix(vector);
                break;

              default:
                throw new RangeError("Matrix for function diag must be 2 dimensional");
            }
        };
        /**
 * Create an identity matrix with size m x n
 *
 *     eye(m)
 *     eye(m, n)
 *
 * TODO: more documentation on eye
 *
 * @param {...Number | Matrix | Array} size
 * @return {Matrix} matrix
 */
        math.eye = function eye(size) {
            var args = util.argsToArray(arguments);
            if (args.length == 0) {
                args = [ 1, 1 ];
            } else if (args.length == 1) {
                args[1] = args[0];
            } else if (args.length > 2) {
                throw newArgumentsError("eye", args.length, 0, 2);
            }
            var rows = args[0], cols = args[1];
            if (!isNumber(rows) || !isInteger(rows) || rows < 1) {
                throw new Error("Parameters in function eye must be positive integers");
            }
            if (cols) {
                if (!isNumber(cols) || !isInteger(cols) || cols < 1) {
                    throw new Error("Parameters in function eye must be positive integers");
                }
            }
            // create and args the matrix
            var matrix = new Matrix();
            matrix.resize(args);
            // fill in ones on the diagonal
            var min = math.min(args);
            var data = matrix.valueOf();
            for (var d = 0; d < min; d++) {
                data[d][d] = 1;
            }
            return matrix;
        };
        /**
 * Calculate the inverse of a matrix
 *
 *     inv(x)
 *
 * TODO: more documentation on inv
 *
 * @param {Array | Matrix} x
 * @return {Array | Matrix} inv
 */
        math.inv = function inv(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("inv", arguments.length, 1);
            }
            var size = math.size(x);
            switch (size.length) {
              case 0:
                // scalar
                return math.divide(1, x);
                break;

              case 1:
                // vector
                if (size[0] == 1) {
                    if (x instanceof Matrix) {
                        return new Matrix([ math.divide(1, x.valueOf()[0]) ]);
                    } else {
                        return [ math.divide(1, x[0]) ];
                    }
                } else {
                    throw new RangeError("Matrix must be square " + "(size: " + math.format(size) + ")");
                }
                break;

              case 2:
                // two dimensional array
                var rows = size[0];
                var cols = size[1];
                if (rows == cols) {
                    if (x instanceof Matrix) {
                        return new Matrix(_inv(x.valueOf(), rows, cols));
                    } else {
                        // return an Array
                        return _inv(x, rows, cols);
                    }
                } else {
                    throw new RangeError("Matrix must be square " + "(size: " + math.format(size) + ")");
                }
                break;

              default:
                // multi dimensional array
                throw new RangeError("Matrix must be two dimensional " + "(size: " + math.format(size) + ")");
            }
        };
        /**
 * Calculate the inverse of a square matrix
 * @param {Array[]} matrix  A square matrix
 * @param {Number} rows     Number of rows
 * @param {Number} cols     Number of columns, must equal rows
 * @return {Array[]} inv    Inverse matrix
 * @private
 */
        function _inv(matrix, rows, cols) {
            var r, s, f, value, temp, add = math.add, unaryminus = math.unaryminus, multiply = math.multiply, divide = math.divide;
            if (rows == 1) {
                // this is a 1 x 1 matrix
                value = matrix[0][0];
                if (value == 0) {
                    throw Error("Cannot calculate inverse, determinant is zero");
                }
                return [ [ divide(1, value) ] ];
            } else if (rows == 2) {
                // this is a 2 x 2 matrix
                var det = math.det(matrix);
                if (det == 0) {
                    throw Error("Cannot calculate inverse, determinant is zero");
                }
                return [ [ divide(matrix[1][1], det), divide(unaryminus(matrix[0][1]), det) ], [ divide(unaryminus(matrix[1][0]), det), divide(matrix[0][0], det) ] ];
            } else {
                // this is a matrix of 3 x 3 or larger
                // calculate inverse using gauss-jordan elimination
                //      http://en.wikipedia.org/wiki/Gaussian_elimination
                //      http://mathworld.wolfram.com/MatrixInverse.html
                //      http://math.uww.edu/~mcfarlat/inverse.htm
                // make a copy of the matrix (only the arrays, not of the elements)
                var A = matrix.concat();
                for (r = 0; r < rows; r++) {
                    A[r] = A[r].concat();
                }
                // create an identity matrix which in the end will contain the
                // matrix inverse
                var B = math.eye(rows).valueOf();
                // loop over all columns, and perform row reductions
                for (var c = 0; c < cols; c++) {
                    // element Acc should be non zero. if not, swap content
                    // with one of the lower rows
                    r = c;
                    while (r < rows && A[r][c] == 0) {
                        r++;
                    }
                    if (r == rows || A[r][c] == 0) {
                        throw Error("Cannot calculate inverse, determinant is zero");
                    }
                    if (r != c) {
                        temp = A[c];
                        A[c] = A[r];
                        A[r] = temp;
                        temp = B[c];
                        B[c] = B[r];
                        B[r] = temp;
                    }
                    // eliminate non-zero values on the other rows at column c
                    var Ac = A[c], Bc = B[c];
                    for (r = 0; r < rows; r++) {
                        var Ar = A[r], Br = B[r];
                        if (r != c) {
                            // eliminate value at column c and row r
                            if (Ar[c] != 0) {
                                f = divide(unaryminus(Ar[c]), Ac[c]);
                                // add (f * row c) to row r to eliminate the value
                                // at column c
                                for (s = c; s < cols; s++) {
                                    Ar[s] = add(Ar[s], multiply(f, Ac[s]));
                                }
                                for (s = 0; s < cols; s++) {
                                    Br[s] = add(Br[s], multiply(f, Bc[s]));
                                }
                            }
                        } else {
                            // normalize value at Acc to 1,
                            // divide each value on row r with the value at Acc
                            f = Ac[c];
                            for (s = c; s < cols; s++) {
                                Ar[s] = divide(Ar[s], f);
                            }
                            for (s = 0; s < cols; s++) {
                                Br[s] = divide(Br[s], f);
                            }
                        }
                    }
                }
                return B;
            }
        }
        /**
 * Create a matrix filled with ones
 *
 *     ones(n)
 *     ones(m, n)
 *     ones([m, n])
 *     ones([m, n, p, ...])
 *
 * @param {...Number | Array} size
 * @return {Matrix} matrix
 */
        math.ones = function ones(size) {
            var args = util.argsToArray(arguments);
            if (args.length == 0) {
                args = [ 1, 1 ];
            } else if (args.length == 1) {
                args[1] = args[0];
            }
            // create and size the matrix
            var matrix = new Matrix();
            var defaultValue = 1;
            matrix.resize(args, defaultValue);
            return matrix;
        };
        /**
 * Calculate the size of a matrix or scalar
 *
 *     size(x)
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.size = function size(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("size", arguments.length, 1);
            }
            if (isNumber(x) || x instanceof Complex || x instanceof Unit || x == null) {
                return [];
            }
            if (isString(x)) {
                return [ x.length ];
            }
            if (x instanceof Array) {
                return util.size(x);
            }
            if (x instanceof Matrix) {
                return x.size();
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.size(x.valueOf());
            }
            throw newUnsupportedTypeError("size", x);
        };
        /**
 * Remove singleton dimensions from a matrix
 *
 *     squeeze(x)
 *
 * @param {Matrix | Array} x
 * @return {Matrix | Array} res
 */
        math.squeeze = function squeeze(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("squeeze", arguments.length, 1);
            }
            if (x instanceof Array) {
                return _squeezeArray(math.clone(x));
            } else if (x instanceof Matrix) {
                return _squeezeArray(x.toArray());
            } else if (x.valueOf() instanceof Array) {
                return _squeezeArray(math.clone(x.valueOf()));
            } else {
                // scalar
                return math.clone(x);
            }
        };
        /**
 * Recursively squeeze a multi dimensional array
 * @param {Array} array
 * @return {Array} array
 * @private
 */
        function _squeezeArray(array) {
            if (array.length == 1) {
                // squeeze this array
                return _squeezeArray(array[0]);
            } else {
                // process all childs
                for (var i = 0, len = array.length; i < len; i++) {
                    var child = array[i];
                    if (child instanceof Array) {
                        array[i] = _squeezeArray(child);
                    }
                }
                return array;
            }
        }
        /**
 * Get or set a subset of a matrix or string
 *
 * Usage:
 *     var subset = math.subset(value, index)               // retrieve subset
 *     var value = math.subset(value, index, replacement)   // replace subset
 *
 * Where:
 *     {*} value        An array, matrix, or scalar value
 *     {Array} index    An array containing index values
 *     {*} replacement  An array, matrix, or scalar
 *
 * @param args
 * @return res
 */
        math.subset = function subset(args) {
            switch (arguments.length) {
              case 2:
                // get subset
                return _getSubset(arguments[0], arguments[1]);

              case 3:
                // set subset
                return _setSubset(arguments[0], arguments[1], arguments[2]);

              default:
                // wrong number of arguments
                throw newArgumentsError("subset", arguments.length, 2, 3);
            }
        };
        /**
 * Retrieve a subset of an value such as an Array, Matrix, or String
 * @param {*} value            Object from which to get a subset
 * @param {Array[] | Range[] | Number[] | Matrix} index
 *                              Two dimensional array (size 1 x n) containing
 *                              the indexes to be retrieved. Can also be a two
 *                              dimensional Matrix (size 1 x n), or an Array
 *                              (size 1) containing a Range or a Number.
 * @returns {*} subset
 * @private
 */
        function _getSubset(value, index) {
            var matrix, subset;
            if (value instanceof Array || value instanceof Range) {
                matrix = math.matrix(value);
                subset = matrix.get(index);
                return subset.valueOf();
            } else if (value instanceof Matrix) {
                return value.get(index);
            } else if (isString(value)) {
                return _getSubstring(value, index);
            } else {
                // scalar
                matrix = math.matrix([ value ]);
                subset = matrix.get(index);
                return subset.valueOf();
            }
        }
        /**
 * Retrieve a subset of a string
 * @param {String} str          String from which to get a substring
 * @param {Array[] | Range[] | Number[] | Matrix} index
 *                              Two dimensional array (size 1 x n) containing
 *                              the indexes to be retrieved. Can also be a two
 *                              dimensional Matrix (size 1 x n), or an Array
 *                              (size 1) containing a Range or a Number.
 * @returns {string} substring
 * @private
 */
        function _getSubstring(str, index) {
            var i, len;
            index = index.valueOf();
            // cast from matrix or range to array
            if (index.length != 1) {
                throw new RangeError("Dimension mismatch (" + index.length + " != 1)");
            }
            if (index instanceof Array) {
                index = index[0];
            }
            index = index.valueOf();
            // cast from matrix or range to array
            if (!(index instanceof Array)) {
                index = [ index ];
            }
            var substr = "";
            var strLen = str.length;
            for (i = 0, len = index.length; i < len; i++) {
                var index_i = index[i];
                util.validateIndex(index_i, strLen);
                substr += str.charAt(index_i - 1);
            }
            return substr;
        }
        /**
 * Replace a subset in an value such as an Array, Matrix, or String
 * @param {*} value            Object to be replaced
 * @param {Array[] | Range[] | Number[] | Matrix} index
 *                              Two dimensional array (size 1 x n) containing
 *                              the indexes to be replaced. Can also be a two
 *                              dimensional Matrix (size 1 x n), or an Array
 *                              (size 1) containing a Range.
 * @param {String} replacement
 * @returns {*} result
 * @private
 */
        function _setSubset(value, index, replacement) {
            if (value instanceof Array || value instanceof Range) {
                var matrix = math.matrix(math.clone(value));
                matrix.set(index, replacement);
                return matrix.valueOf();
            } else if (value instanceof Matrix) {
                return value.clone().set(index, replacement);
            } else if (isString(value)) {
                return _setSubstring(value, index, replacement);
            } else {
                // scalar
                matrix = math.matrix([ value ]);
                matrix.set(index, replacement);
                if (matrix.isScalar()) {
                    // still a scalar
                    return matrix.toScalar();
                } else {
                    // changed into a matrix. return array
                    return matrix.valueOf();
                }
            }
        }
        /**
 * Replace a substring in a string
 * @param {String} str          String to be replaced
 * @param {Array[] | Range[] | Number[] | Matrix} index
 *                              Two dimensional array (size 1 x n) containing
 *                              the indexes to be replaced. Can also be a two
 *                              dimensional Matrix (size 1 x n), or an Array
 *                              (size 1) containing a Range.
 * @param {String} replacement  Replacement string
 * @returns {string} result
 * @private
 */
        function _setSubstring(str, index, replacement) {
            var i, len;
            index = index.valueOf();
            // cast from matrix or range to array
            if (index.length != 1) {
                throw new RangeError("Dimension mismatch (" + index.length + " != 1)");
            }
            if (index instanceof Array) {
                index = index[0];
            }
            index = index.valueOf();
            // cast from matrix or range to array
            if (!(index instanceof Array)) {
                index = [ index ];
            }
            if (index.length != replacement.length) {
                throw new RangeError("Dimension mismatch " + "(" + index.length + " != " + replacement.length + ")");
            }
            // copy the string into an array with characters
            var strLen = str.length;
            var chars = [];
            for (i = 0; i < strLen; i++) {
                chars[i] = str.charAt(i);
            }
            for (i = 0, len = index.length; i < len; i++) {
                var index_i = index[i];
                util.validateIndex(index_i);
                chars[index_i - 1] = replacement.charAt(i);
            }
            // initialize undefined characters with a space
            if (chars.length > strLen) {
                for (i = strLen - 1, len = chars.length; i < len; i++) {
                    if (!chars[i]) {
                        chars[i] = " ";
                    }
                }
            }
            return chars.join("");
        }
        /**
 * Create the transpose of a matrix
 *
 *     transpose(x)
 *
 * @param {Array | Matrix} x
 * @return {Array | Matrix} transpose
 */
        math.transpose = function transpose(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("transpose", arguments.length, 1);
            }
            var size = math.size(x);
            switch (size.length) {
              case 0:
                // scalar
                return math.clone(x);
                break;

              case 1:
                // vector
                // TODO: is it logic to return a 1 dimensional vector itself as transpose?
                return math.clone(x);
                break;

              case 2:
                // two dimensional array
                var rows = size[1], // index 1 is no error
                cols = size[0], // index 0 is no error
                asMatrix = x instanceof Matrix, array = x.valueOf(), transposed = [], transposedRow, clone = math.clone;
                for (var r = 0; r < rows; r++) {
                    transposedRow = transposed[r] = [];
                    for (var c = 0; c < cols; c++) {
                        transposedRow[c] = clone(array[c][r]);
                    }
                }
                if (cols == 0) {
                    transposed[0] = [];
                }
                return asMatrix ? new Matrix(transposed) : transposed;
                break;

              default:
                // multi dimensional array
                throw new RangeError("Matrix must be two dimensional " + "(size: " + math.format(size) + ")");
            }
        };
        /**
 * create a matrix filled with zeros
 *
 *     zeros(n)
 *     zeros(m, n)
 *     zeros([m, n])
 *     zeros([m, n, p, ...])
 *
 * @param {...Number | Array} size
 * @return {Matrix} matrix
 */
        math.zeros = function zeros(size) {
            var args = util.argsToArray(arguments);
            if (args.length == 0) {
                args = [ 1, 1 ];
            } else if (args.length == 1) {
                args[1] = args[0];
            }
            // create and size the matrix
            var matrix = new Matrix();
            matrix.resize(args);
            return matrix;
        };
        /**
 * Compute the factorial of a value
 *
 *     x!
 *     factorial(x)
 *
 * Factorial only supports an integer value as argument.
 * For matrices, the function is evaluated element wise.
 *
 * @Param {Number | Array | Matrix} x
 * @return {Number | Array | Matrix} res
 */
        math.factorial = function factorial(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("factorial", arguments.length, 1);
            }
            if (isNumber(x)) {
                if (!isInteger(x) || x < 0) {
                    throw new TypeError("Positive integer value expected in function factorial");
                }
                var value = x, res = value;
                value--;
                while (value > 1) {
                    res *= value;
                    value--;
                }
                if (res == 0) {
                    res = 1;
                }
                return res;
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.factorial);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.factorial(x.valueOf());
            }
            throw newUnsupportedTypeError("factorial", x);
        };
        /**
 * Return a random number between 0 and 1
 *
 *     random()
 *
 * @return {Number} res
 */
        math.random = function random() {
            if (arguments.length != 0) {
                throw newArgumentsError("random", arguments.length, 0);
            }
            // TODO: implement parameter min and max
            return Math.random();
        };
        /**
 * Compute the maximum value of a list of values
 *
 *     max(a, b, c, ...)
 *     max([a, b, c, ...])
 *
 * @param {... *} args  A single matrix or or multiple scalar values
 * @return {*} res
 */
        math.max = function max(args) {
            if (arguments.length == 0) {
                throw new Error("Function max requires one or more parameters (0 provided)");
            }
            if (args instanceof Array || args instanceof Matrix || args instanceof Range) {
                // max([a, b, c, d, ...]])
                if (arguments.length > 1) {
                    throw Error("Wrong number of parameters (1 matrix or multiple scalars expected)");
                }
                var size = math.size(args);
                if (size.length == 1) {
                    // vector
                    if (args.length == 0) {
                        throw new Error("Cannot calculate max of an empty vector");
                    }
                    return _max(args.valueOf());
                } else if (size.length == 2) {
                    // 2 dimensional matrix
                    if (size[0] == 0 || size[1] == 0) {
                        throw new Error("Cannot calculate max of an empty matrix");
                    }
                    if (args instanceof Array) {
                        return _max2(args, size[0], size[1]);
                    } else if (args instanceof Matrix || args instanceof Range) {
                        return new Matrix(_max2(args.valueOf(), size[0], size[1]));
                    } else {
                        throw newUnsupportedTypeError("max", args);
                    }
                } else {
                    // TODO: implement max for n-dimensional matrices
                    throw new RangeError("Cannot calculate max for multi dimensional matrix");
                }
            } else {
                // max(a, b, c, d, ...)
                return _max(arguments);
            }
        };
        /**
 * Calculate the max of a one dimensional array
 * @param {Array} array
 * @return {Number} max
 * @private
 */
        function _max(array) {
            var larger = math.larger;
            var res = array[0];
            for (var i = 1, iMax = array.length; i < iMax; i++) {
                var value = array[i];
                if (larger(value, res)) {
                    res = value;
                }
            }
            return res;
        }
        /**
 * Calculate the max of a two dimensional array
 * @param {Array} array
 * @param {Number} rows
 * @param {Number} cols
 * @return {Number[]} max
 * @private
 */
        function _max2(array, rows, cols) {
            var larger = math.larger;
            var res = [];
            for (var c = 0; c < cols; c++) {
                var max = array[0][c];
                for (var r = 1; r < rows; r++) {
                    var value = array[r][c];
                    if (larger(value, max)) {
                        max = value;
                    }
                }
                res[c] = max;
            }
            return res;
        }
        /**
 * Compute the minimum value of a list of values
 *
 *     min(a, b, c, ...)
 *     min([a, b, c, ...])
 *
 * @param {... *} args  A single matrix or multiple scalars
 * @return {*} res
 */
        math.min = function min(args) {
            if (arguments.length == 0) {
                throw new Error("Function min requires one or more parameters (0 provided)");
            }
            if (args instanceof Array || args instanceof Matrix || args instanceof Range) {
                // min([a, b, c, d, ...]])
                if (arguments.length > 1) {
                    throw Error("Wrong number of parameters (1 matrix or multiple scalars expected)");
                }
                var size = math.size(args);
                if (size.length == 1) {
                    // vector
                    if (args.length == 0) {
                        throw new Error("Cannot calculate min of an empty vector");
                    }
                    return _min(args.valueOf());
                } else if (size.length == 2) {
                    // 2 dimensional matrix
                    if (size[0] == 0 || size[1] == 0) {
                        throw new Error("Cannot calculate min of an empty matrix");
                    }
                    if (args instanceof Array) {
                        return _min2(args, size[0], size[1]);
                    } else if (args instanceof Matrix || args instanceof Range) {
                        return new Matrix(_min2(args.valueOf(), size[0], size[1]));
                    } else {
                        throw newUnsupportedTypeError("min", args);
                    }
                } else {
                    // TODO: implement min for n-dimensional matrices
                    throw new RangeError("Cannot calculate min for multi dimensional matrix");
                }
            } else {
                // min(a, b, c, d, ...)
                return _min(arguments);
            }
        };
        /**
 * Calculate the min of a one dimensional array
 * @param {Array} array
 * @return {Number} min
 * @private
 */
        function _min(array) {
            var smaller = math.smaller;
            var res = array[0];
            for (var i = 1, iMax = array.length; i < iMax; i++) {
                var value = array[i];
                if (smaller(value, res)) {
                    res = value;
                }
            }
            return res;
        }
        /**
 * Calculate the min of a two dimensional array
 * @param {Array} array
 * @param {Number} rows
 * @param {Number} cols
 * @return {Number[]} min
 * @private
 */
        function _min2(array, rows, cols) {
            var smaller = math.smaller;
            var res = [];
            for (var c = 0; c < cols; c++) {
                var min = array[0][c];
                for (var r = 1; r < rows; r++) {
                    var value = array[r][c];
                    if (smaller(value, min)) {
                        min = value;
                    }
                }
                res[c] = min;
            }
            return res;
        }
        /**
 * Calculate the inverse cosine of a value
 *
 *     acos(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/InverseCosine.html
 */
        math.acos = function acos(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("acos", arguments.length, 1);
            }
            if (isNumber(x)) {
                if (x >= -1 && x <= 1) {
                    return Math.acos(x);
                } else {
                    return math.acos(new Complex(x, 0));
                }
            }
            if (x instanceof Complex) {
                // acos(z) = 0.5*pi + i*log(iz + sqrt(1-z^2))
                var temp1 = new Complex(x.im * x.im - x.re * x.re + 1, -2 * x.re * x.im);
                var temp2 = math.sqrt(temp1);
                var temp3 = new Complex(temp2.re - x.im, temp2.im + x.re);
                var temp4 = math.log(temp3);
                // 0.5*pi = 1.5707963267948966192313216916398
                return new Complex(1.5707963267948966 - temp4.im, temp4.re);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.acos);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.acos(x.valueOf());
            }
            throw newUnsupportedTypeError("acos", x);
        };
        /**
 * Calculate the inverse sine of a value
 *
 *     asin(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/InverseSine.html
 */
        math.asin = function asin(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("asin", arguments.length, 1);
            }
            if (isNumber(x)) {
                if (x >= -1 && x <= 1) {
                    return Math.asin(x);
                } else {
                    return math.asin(new Complex(x, 0));
                }
            }
            if (x instanceof Complex) {
                // asin(z) = -i*log(iz + sqrt(1-z^2))
                var re = x.re;
                var im = x.im;
                var temp1 = new Complex(im * im - re * re + 1, -2 * re * im);
                var temp2 = math.sqrt(temp1);
                var temp3 = new Complex(temp2.re - im, temp2.im + re);
                var temp4 = math.log(temp3);
                return new Complex(temp4.im, -temp4.re);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.asin);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.asin(x.valueOf());
            }
            throw newUnsupportedTypeError("asin", x);
        };
        /**
 * Calculate the inverse tangent of a value
 *
 *     atan(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/InverseTangent.html
 */
        math.atan = function atan(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("atan", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.atan(x);
            }
            if (x instanceof Complex) {
                // atan(z) = 1/2 * i * (ln(1-iz) - ln(1+iz))
                var re = x.re;
                var im = x.im;
                var den = re * re + (1 - im) * (1 - im);
                var temp1 = new Complex((1 - im * im - re * re) / den, -2 * re / den);
                var temp2 = math.log(temp1);
                return new Complex(-.5 * temp2.im, .5 * temp2.re);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.atan);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.atan(x.valueOf());
            }
            throw newUnsupportedTypeError("atan", x);
        };
        /**
 * Computes the principal value of the arc tangent of y/x in radians
 *
 *     atan2(y, x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Array | Matrix} y
 * @param {Number | Complex | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/InverseTangent.html
 */
        math.atan2 = function atan2(y, x) {
            if (arguments.length != 2) {
                throw newArgumentsError("atan2", arguments.length, 2);
            }
            if (isNumber(y)) {
                if (isNumber(x)) {
                    return Math.atan2(y, x);
                }
            } else if (y instanceof Complex) {
                if (isNumber(x)) {
                    return Math.atan2(y.re, x);
                }
            }
            if (y instanceof Array || y instanceof Matrix || x instanceof Array || x instanceof Matrix) {
                return util.map2(y, x, math.atan2);
            }
            if (x.valueOf() !== x || y.valueOf() !== y) {
                // fallback on the objects primitive values
                return math.atan2(y.valueOf(), x.valueOf());
            }
            throw newUnsupportedTypeError("atan2", y, x);
        };
        /**
 * Calculate the cosine of a value
 *
 *     cos(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/Cosine.html
 */
        math.cos = function cos(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("cos", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.cos(x);
            }
            if (x instanceof Complex) {
                // cos(z) = (exp(iz) + exp(-iz)) / 2
                return new Complex(.5 * Math.cos(x.re) * (Math.exp(-x.im) + Math.exp(x.im)), .5 * Math.sin(x.re) * (Math.exp(-x.im) - Math.exp(x.im)));
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function cos is no angle");
                }
                return Math.cos(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.cos);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.cos(x.valueOf());
            }
            throw newUnsupportedTypeError("cos", x);
        };
        /**
 * Calculate the cotangent of a value. cot(x) is defined as 1 / tan(x)
 *
 *     cot(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.cot = function cot(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("cot", arguments.length, 1);
            }
            if (isNumber(x)) {
                return 1 / Math.tan(x);
            }
            if (x instanceof Complex) {
                var den = Math.exp(-4 * x.im) - 2 * Math.exp(-2 * x.im) * Math.cos(2 * x.re) + 1;
                return new Complex(2 * Math.exp(-2 * x.im) * Math.sin(2 * x.re) / den, (Math.exp(-4 * x.im) - 1) / den);
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function cot is no angle");
                }
                return 1 / Math.tan(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.cot);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.cot(x.valueOf());
            }
            throw newUnsupportedTypeError("cot", x);
        };
        /**
 * Calculate the cosecant of a value, csc(x) = 1/sin(x)
 *
 *     csc(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.csc = function csc(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("csc", arguments.length, 1);
            }
            if (isNumber(x)) {
                return 1 / Math.sin(x);
            }
            if (x instanceof Complex) {
                // csc(z) = 1/sin(z) = (2i) / (exp(iz) - exp(-iz))
                var den = .25 * (Math.exp(-2 * x.im) + Math.exp(2 * x.im)) - .5 * Math.cos(2 * x.re);
                return new Complex(.5 * Math.sin(x.re) * (Math.exp(-x.im) + Math.exp(x.im)) / den, .5 * Math.cos(x.re) * (Math.exp(-x.im) - Math.exp(x.im)) / den);
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function csc is no angle");
                }
                return 1 / Math.sin(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.csc);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.csc(x.valueOf());
            }
            throw newUnsupportedTypeError("csc", x);
        };
        /**
 * Calculate the secant of a value, sec(x) = 1/cos(x)
 *
 *     sec(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 */
        math.sec = function sec(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("sec", arguments.length, 1);
            }
            if (isNumber(x)) {
                return 1 / Math.cos(x);
            }
            if (x instanceof Complex) {
                // sec(z) = 1/cos(z) = 2 / (exp(iz) + exp(-iz))
                var den = .25 * (Math.exp(-2 * x.im) + Math.exp(2 * x.im)) + .5 * Math.cos(2 * x.re);
                return new Complex(.5 * Math.cos(x.re) * (Math.exp(-x.im) + Math.exp(x.im)) / den, .5 * Math.sin(x.re) * (Math.exp(x.im) - Math.exp(-x.im)) / den);
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function sec is no angle");
                }
                return 1 / Math.cos(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.sec);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.sec(x.valueOf());
            }
            throw newUnsupportedTypeError("sec", x);
        };
        /**
 * Calculate the sine of a value
 *
 *     sin(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/Sine.html
 */
        math.sin = function sin(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("sin", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.sin(x);
            }
            if (x instanceof Complex) {
                return new Complex(.5 * Math.sin(x.re) * (Math.exp(-x.im) + Math.exp(x.im)), .5 * Math.cos(x.re) * (Math.exp(x.im) - Math.exp(-x.im)));
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function cos is no angle");
                }
                return Math.sin(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.sin);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.sin(x.valueOf());
            }
            throw newUnsupportedTypeError("sin", x);
        };
        /**
 * Calculate the tangent of a value
 *
 *     tan(x)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Number | Complex | Unit | Array | Matrix} x
 * @return {Number | Complex | Array | Matrix} res
 *
 * @see http://mathworld.wolfram.com/Tangent.html
 */
        math.tan = function tan(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("tan", arguments.length, 1);
            }
            if (isNumber(x)) {
                return Math.tan(x);
            }
            if (x instanceof Complex) {
                var den = Math.exp(-4 * x.im) + 2 * Math.exp(-2 * x.im) * Math.cos(2 * x.re) + 1;
                return new Complex(2 * Math.exp(-2 * x.im) * Math.sin(2 * x.re) / den, (1 - Math.exp(-4 * x.im)) / den);
            }
            if (x instanceof Unit) {
                if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) {
                    throw new TypeError("Unit in function tan is no angle");
                }
                return Math.tan(x.value);
            }
            if (x instanceof Array || x instanceof Matrix) {
                return util.map(x, math.tan);
            }
            if (x.valueOf() !== x) {
                // fallback on the objects primitive value
                return math.tan(x.valueOf());
            }
            throw newUnsupportedTypeError("tan", x);
        };
        /**
 * Change the unit of a value.
 *
 *     x in unit
 *     in(x, unit)
 *
 * For matrices, the function is evaluated element wise.
 *
 * @param {Unit | Array | Matrix} x
 * @param {Unit | Array | Matrix} unit
 * @return {Unit | Array | Matrix} res
 */
        math["in"] = function unit_in(x, unit) {
            if (arguments.length != 2) {
                throw newArgumentsError("in", arguments.length, 2);
            }
            if (x instanceof Unit) {
                if (unit instanceof Unit || isString(unit)) {
                    return x["in"](unit);
                }
            }
            // TODO: add support for string, in that case, convert to unit
            if (x instanceof Array || x instanceof Matrix || unit instanceof Array || unit instanceof Matrix) {
                return util.map2(x, unit, math["in"]);
            }
            if (x.valueOf() !== x || unit.valueOf() !== unit) {
                // fallback on the objects primitive value
                return math["in"](x.valueOf(), unit.valueOf());
            }
            throw newUnsupportedTypeError("in", x, unit);
        };
        /**
 * Clone an object
 *
 *     clone(x)
 *
 * @param {*} x
 * @return {*} clone
 */
        math.clone = function clone(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("clone", arguments.length, 1);
            }
            if (x == null) {
                // null or undefined
                return x;
            }
            if (typeof x.clone === "function") {
                return x.clone();
            }
            if (isNumber(x) || isString(x) || isBoolean(x)) {
                return x;
            }
            if (x instanceof Array) {
                var c = math.clone;
                return x.map(function(value) {
                    return c(value);
                });
            }
            if (x instanceof Object) {
                return util.mapObject(x, math.clone);
            }
            throw newUnsupportedTypeError("clone", x);
        };
        /**
 * Evaluate an expression.
 *
 * Syntax:
 *
 *     math.eval(expr)
 *     math.eval(expr, scope)
 *     math.eval([expr1, expr2, expr3, ...])
 *     math.eval([expr1, expr2, expr3, ...], scope)
 *
 * Example:
 *
 *     math.eval('(2+3)/4');                // 1.25
 *     math.eval('sqrt(3^2 + 4^2)');        // 5
 *     math.eval('sqrt(-4)');               // 2i
 *     math.eval(['a=3', 'b=4', 'a*b']);,   // [3, 4, 12]
 *
 *     var scope = {a:3, b:4};
 *     math.eval('a * b', scope);           // 12
 *
 * @param {String | String[] | Matrix} expr
 * @param {math.expr.Scope | Object} [scope]
 * @return {*} res
 * @throws {Error}
 */
        math.eval = function(expr, scope) {
            if (arguments.length != 1 && arguments.length != 2) {
                throw newArgumentsError("eval", arguments.length, 1, 2);
            }
            // instantiate a scope
            var evalScope;
            if (scope) {
                if (scope instanceof math.expr.Scope) {
                    evalScope = scope;
                } else {
                    evalScope = new math.expr.Scope(scope);
                }
            } else {
                evalScope = new math.expr.Scope();
            }
            if (isString(expr)) {
                // evaluate a single expression
                var node = math.parse(expr, evalScope);
                return node.eval();
            } else if (expr instanceof Array || expr instanceof Matrix) {
                // evaluate an array or matrix with expressions
                return util.map(expr, function(elem) {
                    var node = math.parse(elem, evalScope);
                    return node.eval();
                });
            } else {
                // oops
                throw new TypeError("String or matrix expected");
            }
        };
        /**
 * Format a value of any type into a string. Interpolate values into the string.
 * Numbers are rounded off to a maximum number of 5 digits by default.
 * Usage:
 *     math.format(value)
 *     math.format(template, object)
 *
 * Example usage:
 *     math.format(2/7);                // '0.28571'
 *     math.format(new Complex(2, 3));  // '2 + 3i'
 *     math.format('Hello $name! The date is $date', {
 *         name: 'user',
 *         date: new Date().toISOString().substring(0, 10)
 *     });                              // 'hello user! The date is 2013-03-23'
 *
 * @param {String} template
 * @param {Object} values
 * @return {String} str
 */
        math.format = function format(template, values) {
            var num = arguments.length;
            if (num != 1 && num != 2) {
                throw newArgumentsError("format", num, 1, 2);
            }
            if (num == 1) {
                // just format a value as string
                var value = arguments[0];
                if (isNumber(value)) {
                    return util.formatNumber(value, math.options.precision);
                }
                if (value instanceof Array) {
                    return util.formatArray(value);
                }
                if (isString(value)) {
                    return '"' + value + '"';
                }
                if (value instanceof Object) {
                    return value.toString();
                }
                return String(value);
            } else {
                if (!isString(template)) {
                    throw new TypeError("String expected as first parameter in function format");
                }
                if (!(values instanceof Object)) {
                    throw new TypeError("Object expected as first parameter in function format");
                }
                // format values into a string
                return template.replace(/\$([\w\.]+)/g, function(original, key) {
                    var keys = key.split(".");
                    var value = values[keys.shift()];
                    while (keys.length && value != undefined) {
                        var k = keys.shift();
                        value = k ? value[k] : value + ".";
                    }
                    return value != undefined ? value : original;
                });
            }
        };
        /**
 * Import functions from an object or a file
 * @param {function | String | Object} object
 * @param {Object} [options]        Available options:
 *                                  {Boolean} override
 *                                      If true, existing functions will be
 *                                      overwritten. False by default.
 *                                  {Boolean} wrap
 *                                      If true (default), the functions will
 *                                      be wrapped in a wrapper function which
 *                                      converts data types like Matrix to
 *                                      primitive data types like Array.
 *                                      The wrapper is needed when extending
 *                                      math.js with libraries which do not
 *                                      support the math.js data types.
 */
        // TODO: return status information
        math["import"] = function math_import(object, options) {
            var name;
            var opts = {
                override: false,
                wrap: true
            };
            if (options && options instanceof Object) {
                util.extend(opts, options);
            }
            if (isString(object)) {
                // a string with a filename
                if (typeof require !== "undefined") {
                    // load the file using require
                    var _module = require(object);
                    math["import"](_module);
                } else {
                    throw new Error("Cannot load file: require not available.");
                }
            } else if (isSupportedType(object)) {
                // a single function
                name = object.name;
                if (name) {
                    if (opts.override || math[name] === undefined) {
                        _import(name, object, opts);
                    }
                } else {
                    throw new Error("Cannot import an unnamed function or object");
                }
            } else if (object instanceof Object) {
                // a map with functions
                for (name in object) {
                    if (object.hasOwnProperty(name)) {
                        var value = object[name];
                        if (isSupportedType(value)) {
                            _import(name, value, opts);
                        } else {
                            math["import"](value);
                        }
                    }
                }
            }
        };
        /**
 * Add a property to the math namespace and create a chain proxy for it.
 * @param {String} name
 * @param {*} value
 * @param {Object} options  See import for a description of the options
 * @private
 */
        function _import(name, value, options) {
            if (options.override || math[name] === undefined) {
                // add to math namespace
                if (options.wrap && typeof value === "function") {
                    // create a wrapper around the function
                    math[name] = function() {
                        var args = [];
                        for (var i = 0, len = arguments.length; i < len; i++) {
                            args[i] = arguments[i].valueOf();
                        }
                        return value.apply(math, args);
                    };
                } else {
                    // just create a link to the function or value
                    math[name] = value;
                }
                // create a proxy for the Selector
                createSelectorProxy(name, value);
            }
        }
        /**
 * Check whether given object is a supported type
 * @param object
 * @return {Boolean}
 * @private
 */
        function isSupportedType(object) {
            return typeof object == "function" || isNumber(object) || isString(object) || object instanceof Complex || object instanceof Unit;
        }
        (function() {
            /**
     * Parse an expression. Returns a node tree, which can be evaluated by
     * invoking node.eval();
     *
     * Syntax:
     *
     *     math.parse(expr)
     *     math.parse(expr, scope)
     *     math.parse([expr1, expr2, expr3, ...])
     *     math.parse([expr1, expr2, expr3, ...], scope)
     *
     * Example:
     *
     *     var node = math.parse('sqrt(3^2 + 4^2)');
     *     node.eval(); // 5
     *
     *     var scope = {a:3, b:4}
     *     var node = math.parse('a * b', scope); // 12
     *     node.eval(); // 12
     *     scope.a = 5;
     *     node.eval(); // 20
     *
     *     var nodes = math.parse(['a = 3', 'b = 4', 'a * b']);
     *     nodes[2].eval(); // 12
     *
     * @param {String | String[] | Matrix} expr
     * @param {math.expr.Scope | Object} [scope]
     * @return {Node | Node[]} node
     * @throws {Error}
     */
            math.parse = function(expr, scope) {
                if (arguments.length != 1 && arguments.length != 2) {
                    throw newArgumentsError("parse", arguments.length, 1, 2);
                }
                // instantiate a scope
                var parseScope;
                if (scope) {
                    if (scope instanceof math.expr.Scope) {
                        parseScope = scope;
                    } else {
                        parseScope = new math.expr.Scope(scope);
                    }
                } else {
                    parseScope = new math.expr.Scope();
                }
                if (isString(expr)) {
                    // parse a single expression
                    expression = expr || "";
                    return parse_start(parseScope);
                } else if (expr instanceof Array || expr instanceof Matrix) {
                    // parse an array or matrix with expressions
                    return util.map(expr, function(elem) {
                        expression = elem || "";
                        return parse_start(parseScope);
                    });
                } else {
                    // oops
                    throw new TypeError("String or matrix expected");
                }
            };
            // token types enumeration
            var TOKENTYPE = {
                NULL: 0,
                DELIMITER: 1,
                NUMBER: 2,
                SYMBOL: 3,
                UNKNOWN: 4
            };
            var expression = "";
            // current expression
            var index = 0;
            // current index in expr
            var c = "";
            // current token character in expr
            var token = "";
            // current token
            var token_type = TOKENTYPE.NULL;
            // type of the token
            /**
     * Get the next character from the expression.
     * The character is stored into the char t.
     * If the end of the expression is reached, the function puts an empty
     * string in t.
     * @private
     */
            function getChar() {
                index++;
                c = expression.charAt(index);
            }
            /**
     * Get the first character from the expression.
     * The character is stored into the char t.
     * If the end of the expression is reached, the function puts an empty
     * string in t.
     * @private
     */
            function getFirstChar() {
                index = 0;
                c = expression.charAt(0);
            }
            /**
     * Get next token in the current string expr.
     * Uses the Parser data expr, e, token, t, token_type and err
     * The token and token type are available at token_type and token
     * @private
     */
            function getToken() {
                token_type = TOKENTYPE.NULL;
                token = "";
                // skip over whitespaces
                while (c == " " || c == "	") {
                    // space or tab
                    getChar();
                }
                // skip comment
                if (c == "#") {
                    while (c != "\n" && c != "") {
                        getChar();
                    }
                }
                // check for end of expression
                if (c == "") {
                    // token is still empty
                    token_type = TOKENTYPE.DELIMITER;
                    return;
                }
                // check for factorial character !, and unequal operator !=
                if (c == "!") {
                    token_type = TOKENTYPE.DELIMITER;
                    token += c;
                    getChar();
                    // TODO: solve operators consisting of of two characters in a more generic way
                    if (c == "=") {
                        token += c;
                        getChar();
                    }
                    return;
                }
                // check for minus, comma, parentheses, quotes, newline, semicolon
                if (c == "-" || c == "," || c == "(" || c == ")" || c == "[" || c == "]" || c == '"' || c == "'" || c == "\n" || c == ";" || c == ":") {
                    token_type = TOKENTYPE.DELIMITER;
                    token += c;
                    getChar();
                    return;
                }
                // check for operators (delimiters)
                if (isDelimiter(c)) {
                    token_type = TOKENTYPE.DELIMITER;
                    while (isDelimiter(c)) {
                        token += c;
                        getChar();
                    }
                    return;
                }
                // check for a number
                if (isDigitDot(c)) {
                    token_type = TOKENTYPE.NUMBER;
                    // get number, can have a single dot
                    if (c == ".") {
                        token += c;
                        getChar();
                        if (!isDigit(c)) {
                            // this is no legal number, it is just a dot
                            token_type = TOKENTYPE.UNKNOWN;
                        }
                    } else {
                        while (isDigit(c)) {
                            token += c;
                            getChar();
                        }
                        if (c == ".") {
                            token += c;
                            getChar();
                        }
                    }
                    while (isDigit(c)) {
                        token += c;
                        getChar();
                    }
                    // check for scientific notation like "2.3e-4" or "1.23e50"
                    if (c == "E" || c == "e") {
                        token += c;
                        getChar();
                        if (c == "+" || c == "-") {
                            token += c;
                            getChar();
                        }
                        // Scientific notation MUST be followed by an exponent
                        if (!isDigit(c)) {
                            // this is no legal number, exponent is missing.
                            token_type = TOKENTYPE.UNKNOWN;
                        }
                        while (isDigit(c)) {
                            token += c;
                            getChar();
                        }
                    }
                    return;
                }
                // check for variables or functions
                if (isAlpha(c)) {
                    token_type = TOKENTYPE.SYMBOL;
                    while (isAlpha(c) || isDigit(c)) {
                        token += c;
                        getChar();
                    }
                    return;
                }
                // something unknown is found, wrong characters -> a syntax error
                token_type = TOKENTYPE.UNKNOWN;
                while (c != "") {
                    token += c;
                    getChar();
                }
                throw createSyntaxError('Syntax error in part "' + token + '"');
            }
            /**
     * checks if the given char c is a delimiter
     * minus is not checked in this method (can be unary minus)
     * @param {String} c   a string with one character
     * @return {Boolean}
     * @private
     */
            function isDelimiter(c) {
                return c == "&" || c == "|" || c == "<" || c == ">" || c == "=" || c == "+" || c == "/" || c == "*" || c == "%" || c == "^" || c == "," || c == ";" || c == "\n" || c == "!" || c == "'";
            }
            /**
     * Check if a given name is valid
     * if not, an error is thrown
     * @param {String} name
     * @return {boolean} valid
     * @private
     */
            function isValidSymbolName(name) {
                for (var i = 0, iMax = name.length; i < iMax; i++) {
                    var c = name.charAt(i);
                    //var valid = (isAlpha(c) || (i > 0 && isDigit(c))); // TODO: allow digits in symbol name
                    var valid = isAlpha(c);
                    if (!valid) {
                        return false;
                    }
                }
                return true;
            }
            /**
     * checks if the given char c is a letter (upper or lower case)
     * or underscore
     * @param {String} c   a string with one character
     * @return {Boolean}
     * @private
     */
            function isAlpha(c) {
                return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c == "_";
            }
            /**
     * checks if the given char c is a digit or dot
     * @param {String} c   a string with one character
     * @return {Boolean}
     * @private
     */
            function isDigitDot(c) {
                return c >= "0" && c <= "9" || c == ".";
            }
            /**
     * checks if the given char c is a digit
     * @param {String} c   a string with one character
     * @return {Boolean}
     * @private
     */
            function isDigit(c) {
                return c >= "0" && c <= "9";
            }
            /**
     * Start of the parse levels below, in order of precedence
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_start(scope) {
                // get the first character in expression
                getFirstChar();
                getToken();
                var node;
                if (token == "") {
                    // empty expression
                    node = new ConstantNode(undefined);
                } else {
                    node = parse_block(scope);
                }
                // check for garbage at the end of the expression
                // an expression ends with a empty character '' and token_type DELIMITER
                if (token != "") {
                    if (token_type == TOKENTYPE.DELIMITER) {
                        // user entered a not existing operator like "//"
                        // TODO: give hints for aliases, for example with "<>" give as hint " did you mean != ?"
                        throw createError("Unknown operator " + token);
                    } else {
                        throw createSyntaxError('Unexpected part "' + token + '"');
                    }
                }
                return node;
            }
            /**
     * Parse a block with expressions. Expressions can be separated by a newline
     * character '\n', or by a semicolon ';'. In case of a semicolon, no output
     * of the preceding line is returned.
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_block(scope) {
                var node, block, visible;
                if (token != "\n" && token != ";" && token != "") {
                    node = parse_ans(scope);
                }
                while (token == "\n" || token == ";") {
                    if (!block) {
                        // initialize the block
                        block = new BlockNode();
                        if (node) {
                            visible = token != ";";
                            block.add(node, visible);
                        }
                    }
                    getToken();
                    if (token != "\n" && token != ";" && token != "") {
                        node = parse_ans(scope);
                        visible = token != ";";
                        block.add(node, visible);
                    }
                }
                if (block) {
                    return block;
                }
                if (!node) {
                    node = parse_ans(scope);
                }
                return node;
            }
            /**
     * Parse assignment of ans.
     * Ans is assigned when the expression itself is no variable or function
     * assignment
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_ans(scope) {
                var expression = parse_function_assignment(scope);
                // create a variable definition for ans
                var name = "ans";
                return new AssignmentNode(name, expression, scope);
            }
            /**
     * Parse a function assignment like "function f(a,b) = a*b"
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_function_assignment(scope) {
                // TODO: keyword 'function' must become a reserved keyword
                if (token_type == TOKENTYPE.SYMBOL && token == "function") {
                    // get function name
                    getToken();
                    if (token_type != TOKENTYPE.SYMBOL) {
                        throw createSyntaxError("Function name expected");
                    }
                    var name = token;
                    // get parenthesis open
                    getToken();
                    if (token != "(") {
                        throw createSyntaxError("Opening parenthesis ( expected");
                    }
                    // get function variables
                    var functionScope = scope.createSubScope();
                    var variables = [];
                    while (true) {
                        getToken();
                        if (token_type == TOKENTYPE.SYMBOL) {
                            // store variable name
                            variables.push(token);
                        } else {
                            throw createSyntaxError("Variable name expected");
                        }
                        getToken();
                        if (token == ",") {} else if (token == ")") {
                            // end of variable list encountered. break loop
                            break;
                        } else {
                            throw createSyntaxError('Comma , or closing parenthesis ) expected"');
                        }
                    }
                    getToken();
                    if (token != "=") {
                        throw createSyntaxError("Equal sign = expected");
                    }
                    // parse the expression, with the correct function scope
                    getToken();
                    var expression = parse_assignment(functionScope);
                    return new FunctionNode(name, variables, expression, functionScope, scope);
                }
                return parse_assignment(scope);
            }
            /**
     * Assignment of a variable, can be a variable like "a=2.3" or a updating an
     * existing variable like "matrix(2,3:5)=[6,7,8]"
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_assignment(scope) {
                var name, params, paramScopes, expr;
                /* TODO: cleanup? or use? see comments further down
         var linkExisted = false;
         if (token_type == TOKENTYPE.SYMBOL) {
         linkExisted = scope.has(token);
         }
         */
                var node = parse_range(scope);
                if (token == "=") {
                    if (node instanceof SymbolNode) {
                        // TODO: remove link when it was undefined before we parsed this expression?
                        // we parsed the assignment as if it where an expression instead,
                        // therefore, a link was created to the symbol. This link must
                        // be cleaned up again, and only if it wasn't existing before
                        // parse the expression, with the correct function scope
                        getToken();
                        name = node.name;
                        params = null;
                        expr = parse_assignment(scope);
                        return new AssignmentNode(name, expr, scope);
                    } else if (node instanceof ParamsNode && node.object instanceof SymbolNode) {
                        // TODO: remove link when it was undefined before we parsed this expression?
                        // we parsed the assignment as if it where an expression instead,
                        // therefore, a link was created to the symbol. This link must
                        // be cleaned up again, and only if it wasn't existing before
                        // parse the expression, with the correct function scope
                        getToken();
                        name = node.object.name;
                        params = node.params;
                        paramScopes = node.paramScopes;
                        expr = parse_assignment(scope);
                        return new UpdateNode(name, params, paramScopes, expr, scope);
                    } else {
                        throw createSyntaxError("Symbol expected at the left hand side " + "of assignment operator =");
                    }
                }
                return node;
            }
            /**
     * parse range, "start:end", "start:step:end", ":", "start:", ":end", etc
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_range(scope) {
                var node, name, fn, params = [];
                if (token == ":") {
                    // implicit start=1
                    node = new ConstantNode(1);
                } else {
                    // explicit start
                    node = parse_conditions(scope);
                }
                if (token == ":") {
                    params.push(node);
                    // parse step and end
                    while (token == ":") {
                        getToken();
                        if (token == ")" || token == "," || token == "") {
                            // implicit end
                            params.push(new SymbolNode("end", scope));
                        } else {
                            // explicit end
                            params.push(parse_conditions(scope));
                        }
                    }
                    if (params.length) {
                        // create a range constructor
                        name = "range";
                        fn = math.range;
                        node = new OperatorNode(name, fn, params);
                    }
                }
                return node;
            }
            /**
     * conditions like and, or, in
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_conditions(scope) {
                var node, operators, name, fn, params;
                node = parse_bitwise_conditions(scope);
                // TODO: precedence of And above Or?
                // TODO: implement a method for unit to number conversion
                operators = {
                    "in": "in"
                };
                while (operators[token] !== undefined) {
                    name = token;
                    fn = math[operators[name]];
                    getToken();
                    params = [ node, parse_bitwise_conditions(scope) ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * conditional operators and bitshift
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_bitwise_conditions(scope) {
                var node = parse_comparison(scope);
                /* TODO: implement bitwise conditions
         var operators = {
         '&' : 'bitwiseand',
         '|' : 'bitwiseor',
         // todo: bitwise xor?
         '<<': 'bitshiftleft',
         '>>': 'bitshiftright'
         };
         while (operators[token] !== undefined) {
         var name = token;
         var fn = math[operators[name]];

         getToken();
         var params = [node, parse_comparison()];
         node = new OperatorNode(name, fn, params);
         }
         */
                return node;
            }
            /**
     * comparison operators
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_comparison(scope) {
                var node, operators, name, fn, params;
                node = parse_addsubtract(scope);
                operators = {
                    "==": "equal",
                    "!=": "unequal",
                    "<": "smaller",
                    ">": "larger",
                    "<=": "smallereq",
                    ">=": "largereq"
                };
                while (operators[token] !== undefined) {
                    name = token;
                    fn = math[operators[name]];
                    getToken();
                    params = [ node, parse_addsubtract(scope) ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * add or subtract
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_addsubtract(scope) {
                var node, operators, name, fn, params;
                node = parse_multiplydivide(scope);
                operators = {
                    "+": "add",
                    "-": "subtract"
                };
                while (operators[token] !== undefined) {
                    name = token;
                    fn = math[operators[name]];
                    getToken();
                    params = [ node, parse_multiplydivide(scope) ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * multiply, divide, modulus
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_multiplydivide(scope) {
                var node, operators, name, fn, params;
                node = parse_unaryminus(scope);
                operators = {
                    "*": "multiply",
                    "/": "divide",
                    "%": "mod",
                    mod: "mod"
                };
                while (operators[token] !== undefined) {
                    name = token;
                    fn = math[operators[name]];
                    getToken();
                    params = [ node, parse_unaryminus(scope) ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * Unary minus
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_unaryminus(scope) {
                var name, fn, params;
                if (token == "-") {
                    name = token;
                    fn = math.unaryminus;
                    getToken();
                    params = [ parse_pow(scope) ];
                    return new OperatorNode(name, fn, params);
                }
                return parse_pow(scope);
            }
            /**
     * power
     * Node: power operator is right associative
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_pow(scope) {
                var node, leftNode, nodes, operators, name, fn, params;
                nodes = [ parse_factorial(scope) ];
                // stack all operands of a chained power operator (like '2^3^3')
                while (token == "^") {
                    getToken();
                    nodes.push(parse_factorial(scope));
                }
                // evaluate the operands from right to left (right associative)
                node = nodes.pop();
                while (nodes.length) {
                    leftNode = nodes.pop();
                    name = "^";
                    fn = math.pow;
                    params = [ leftNode, node ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * Factorial
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_factorial(scope) {
                var node, name, fn, params;
                node = parse_transpose(scope);
                while (token == "!") {
                    name = token;
                    fn = math.factorial;
                    getToken();
                    params = [ node ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * Transpose
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_transpose(scope) {
                var node, name, fn, params;
                node = parse_plot(scope);
                while (token == "'") {
                    name = token;
                    fn = math.transpose;
                    getToken();
                    params = [ node ];
                    node = new OperatorNode(name, fn, params);
                }
                return node;
            }
            /**
     * parse plot
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_plot(scope) {
                /* TODO: implement plot
         if (token_type == TOKENTYPE.SYMBOL &&
         token == 'plot') {
         getToken();

         // parse the parentheses and parameters of the plot
         // the parameters are something like: plot(sin(x), cos(x), x)
         var functions = [];
         if (token == '(') {
         var plotScope = scope.createSubScope();

         getToken();
         functions.push(parse_assignment(plotScope));

         // parse a list with parameters
         while (token == ',') {
         getToken();
         functions.push(parse_assigment(plotScope));
         }

         if (token != ')') {
         throw createSyntaxError('Parenthesis ) missing');
         }
         getToken();
         }

         // check what the variable of the functions is.
         var variable = undefined;
         var lastFunction = functions[functions.length - 1];
         if (lastFunction) {
         // if the last function is a variable, remove it from the functions list
         // and use its variable func
         var lastIsSymbol = (lastFunction instanceof Arguments);
         if (lastIsSymbol) {
         functions.pop();
         variable = lastFunction.fn;
         }
         }
         return new plot(functions, variable, plotScope);
         }
         */
                return parse_symbol(scope);
            }
            /**
     * parse symbols: functions, variables, constants, units
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_symbol(scope) {
                var node, name;
                if (token_type == TOKENTYPE.SYMBOL) {
                    name = token;
                    getToken();
                    // create a symbol
                    node = new SymbolNode(name, scope);
                    // parse parameters
                    return parse_params(scope, node);
                }
                return parse_string(scope);
            }
            /**
     * parse parameters, enclosed in parenthesis
     * @param {math.expr.Scope} scope
     * @param {Node} node    Node on which to apply the parameters. If there
     *                       are no parameters in the expression, the node
     *                       itself is returned
     * @return {Node} node
     * @private
     */
            function parse_params(scope, node) {
                var params, paramScopes, paramScope;
                while (token == "(") {
                    params = [];
                    paramScopes = [];
                    getToken();
                    if (token != ")") {
                        paramScope = scope.createSubScope();
                        paramScopes.push(paramScope);
                        params.push(parse_range(paramScope));
                        // parse a list with parameters
                        while (token == ",") {
                            getToken();
                            paramScope = scope.createSubScope();
                            paramScopes.push(paramScope);
                            params.push(parse_range(paramScope));
                        }
                    }
                    if (token != ")") {
                        throw createSyntaxError("Parenthesis ) missing");
                    }
                    getToken();
                    node = new ParamsNode(node, params, paramScopes);
                }
                return node;
            }
            /**
     * parse a string.
     * A string is enclosed by double quotes
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_string(scope) {
                var node, str, tPrev;
                if (token == '"') {
                    // string "..."
                    str = "";
                    tPrev = "";
                    while (c != "" && (c != '"' || tPrev == "\\")) {
                        // also handle escape character
                        str += c;
                        tPrev = c;
                        getChar();
                    }
                    getToken();
                    if (token != '"') {
                        throw createSyntaxError('End of string " missing');
                    }
                    getToken();
                    // create constant
                    node = new ConstantNode(str);
                    // parse parameters
                    node = parse_params(scope, node);
                    return node;
                }
                return parse_matrix(scope);
            }
            /**
     * parse the matrix
     * @param {math.expr.Scope} scope
     * @return {Node} A MatrixNode
     * @private
     */
            function parse_matrix(scope) {
                var array, params, r, c, rows, cols;
                if (token == "[") {
                    // matrix [...]
                    // skip newlines
                    getToken();
                    while (token == "\n") {
                        getToken();
                    }
                    // check if this is an empty matrix "[ ]"
                    if (token != "]") {
                        // this is a non-empty matrix
                        params = [];
                        r = 0;
                        c = 0;
                        params[0] = [ parse_assignment(scope) ];
                        // the columns in the matrix are separated by commas, and the rows by dot-comma's
                        while (token == "," || token == ";") {
                            if (token == ",") {
                                c++;
                            } else {
                                r++;
                                c = 0;
                                params[r] = [];
                            }
                            // skip newlines
                            getToken();
                            while (token == "\n") {
                                getToken();
                            }
                            params[r][c] = parse_assignment(scope);
                            // skip newlines
                            while (token == "\n") {
                                getToken();
                            }
                        }
                        rows = params.length;
                        cols = params.length > 0 ? params[0].length : 0;
                        // check if the number of columns matches in all rows
                        for (r = 1; r < rows; r++) {
                            if (params[r].length != cols) {
                                throw createError("Number of columns must match " + "(" + params[r].length + " != " + cols + ")");
                            }
                        }
                        if (token != "]") {
                            throw createSyntaxError("End of matrix ] missing");
                        }
                        getToken();
                        array = new MatrixNode(params);
                    } else {
                        // this is an empty matrix "[ ]"
                        getToken();
                        array = new MatrixNode([ [] ]);
                    }
                    // parse parameters
                    array = parse_params(scope, array);
                    return array;
                }
                return parse_number(scope);
            }
            /**
     * parse a number
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_number(scope) {
                var node, value, number;
                if (token_type == TOKENTYPE.NUMBER) {
                    // this is a number
                    if (token == ".") {
                        number = 0;
                    } else {
                        number = Number(token);
                    }
                    getToken();
                    /* TODO: implicit multiplication?
             // TODO: how to calculate a=3; 2/2a ? is this (2/2)*a or 2/(2*a) ?
             // check for implicit multiplication
             if (token_type == TOKENTYPE.VARIABLE) {
             node = multiply(node, parse_pow());
             }
             //*/
                    if (token_type == TOKENTYPE.SYMBOL) {
                        if (token == "i" || token == "I") {
                            value = new Complex(0, number);
                            getToken();
                            return new ConstantNode(value);
                        }
                        if (Unit.isPlainUnit(token)) {
                            value = new Unit(number, token);
                            getToken();
                            return new ConstantNode(value);
                        }
                        throw createTypeError('Unknown unit "' + token + '"');
                    }
                    // just a regular number
                    node = new ConstantNode(number);
                    // parse parameters
                    node = parse_params(scope, node);
                    return node;
                }
                return parse_parentheses(scope);
            }
            /**
     * parentheses
     * @param {math.expr.Scope} scope
     * @return {Node} node
     * @private
     */
            function parse_parentheses(scope) {
                var node;
                // check if it is a parenthesized expression
                if (token == "(") {
                    // parentheses (...)
                    getToken();
                    node = parse_assignment(scope);
                    // start again
                    if (token != ")") {
                        throw createSyntaxError("Parenthesis ) expected");
                    }
                    getToken();
                    /* TODO: implicit multiplication?
             // TODO: how to calculate a=3; 2/2a ? is this (2/2)*a or 2/(2*a) ?
             // check for implicit multiplication
             if (token_type == TOKENTYPE.VARIABLE) {
             node = multiply(node, parse_pow());
             }
             //*/
                    // parse parameters
                    node = parse_params(scope, node);
                    return node;
                }
                return parse_end(scope);
            }
            /**
     * Evaluated when the expression is not yet ended but expected to end
     * @param {math.expr.Scope} scope
     * @return {Node} res
     * @private
     */
            function parse_end(scope) {
                if (token == "") {
                    // syntax error or unexpected end of expression
                    throw createSyntaxError("Unexpected end of expression");
                } else {
                    throw createSyntaxError("Value expected");
                }
            }
            /**
     * Shortcut for getting the current row value (one based)
     * Returns the line of the currently handled expression
     * @private
     */
            function row() {
                // TODO: also register row number during parsing
                return undefined;
            }
            /**
     * Shortcut for getting the current col value (one based)
     * Returns the column (position) where the last token starts
     * @private
     */
            function col() {
                return index - token.length + 1;
            }
            /**
     * Build up an error message
     * @param {String} message
     * @return {String} message with row and column information
     * @private
     */
            function createErrorMessage(message) {
                var r = row();
                var c = col();
                if (r === undefined) {
                    if (c === undefined) {
                        return message;
                    } else {
                        return message + " (char " + c + ")";
                    }
                } else {
                    return message + " (line " + r + ", char " + c + ")";
                }
            }
            /**
     * Create an error
     * @param {String} message
     * @return {SyntaxError} instantiated error
     * @private
     */
            function createSyntaxError(message) {
                return new SyntaxError(createErrorMessage(message));
            }
            /**
     * Create an error
     * @param {String} message
     * @return {TypeError} instantiated error
     * @private
     */
            function createTypeError(message) {
                return new TypeError(createErrorMessage(message));
            }
            /**
     * Create an error
     * @param {String} message
     * @return {Error} instantiated error
     * @private
     */
            function createError(message) {
                return new Error(createErrorMessage(message));
            }
        })();
        /**
 * Wrap any value in a Selector, allowing to perform chained operations on
 * the value.
 *
 * All methods available in the math.js library can be called upon the selector,
 * and then will be evaluated with the value itself as first argument.
 * The selector can be closed by executing selector.done(), which will return
 * the final value.
 *
 * Example usage:
 *     math.select(3)
 *         .add(4)
 *         .subtract(2)
 *         .done();     // 5
 *     math.select( [[1, 2], [3, 4]] )
 *         .set([1, 1], 8)
 *         .multiply(3)
 *         .done();     // [[24, 6], [9, 12]]
 *
 * The Selector has a number of special functions:
 * - done()     Finalize the chained operation and return the selectors value.
 * - valueOf()  The same as done()
 * - toString() Executes math.format() onto the selectors value, returning
 *              a string representation of the value.
 * - get(...)   Get a subselection of the selectors value. Only applicable when
 *              the value has a method get, for example when value is a Matrix
 *              or Array.
 * - set(...)   Replace a subselection of the selectors value. Only applicable
 *              when the value has a method get, for example when value is a
 *              Matrix or Array.
 *
 * @param {*} value
 * @return {math.type.Selector} selector
 */
        math.select = function select(value) {
            return new math.type.Selector(value);
        };
        /**
 * Determine the type of a variable
 *
 *     typeof(x)
 *
 * @param {*} x
 * @return {String} type  Lower case type, for example "number", "string",
 *                        "array".
 */
        math["typeof"] = function math_typeof(x) {
            if (arguments.length != 1) {
                throw newArgumentsError("typeof", arguments.length, 1);
            }
            var type = typeof x, name;
            if (type == "object") {
                if (x == null) {
                    return "null";
                }
                if (x instanceof Boolean) {
                    return "boolean";
                }
                if (x instanceof Number) {
                    return "number";
                }
                if (x instanceof String) {
                    return "string";
                }
                if (x instanceof Array) {
                    return "array";
                }
                if (x instanceof Date) {
                    return "date";
                }
                if (x.constructor) {
                    // search functions / constants
                    for (name in math) {
                        if (math.hasOwnProperty(name)) {
                            if (x.constructor == math[name]) {
                                return name.toLowerCase();
                            }
                        }
                    }
                    // search data types
                    for (name in math.type) {
                        if (math.type.hasOwnProperty(name)) {
                            if (x.constructor == math.type[name]) {
                                return name.toLowerCase();
                            }
                        }
                    }
                    // try the constructors name as last resort
                    if (x.constructor.name) {
                        return x.constructor.name.toLowerCase();
                    }
                }
            }
            return type;
        };
        /**
 * Compatibility functions for older JavaScript engines
 */
        // Internet Explorer 8 and older does not support Array.indexOf,
        // so we define it here in that case.
        // http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/
        if (!Array.prototype.indexOf) {
            Array.prototype.indexOf = function(obj) {
                for (var i = 0; i < this.length; i++) {
                    if (this[i] == obj) {
                        return i;
                    }
                }
                return -1;
            };
        }
        // Internet Explorer 8 and older does not support Array.forEach,
        // so we define it here in that case.
        // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach
        if (!Array.prototype.forEach) {
            Array.prototype.forEach = function(fn, scope) {
                for (var i = 0, len = this.length; i < len; ++i) {
                    fn.call(scope || this, this[i], i, this);
                }
            };
        }
        // Internet Explorer 8 and older does not support Array.map,
        // so we define it here in that case.
        // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map
        // Production steps of ECMA-262, Edition 5, 15.4.4.19
        // Reference: http://es5.github.com/#x15.4.4.19
        if (!Array.prototype.map) {
            Array.prototype.map = function(callback, thisArg) {
                var T, A, k;
                if (this == null) {
                    throw new TypeError(" this is null or not defined");
                }
                // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
                var O = Object(this);
                // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
                // 3. Let len be ToUint32(lenValue).
                var len = O.length >>> 0;
                // 4. If IsCallable(callback) is false, throw a TypeError exception.
                // See: http://es5.github.com/#x9.11
                if (typeof callback !== "function") {
                    throw new TypeError(callback + " is not a function");
                }
                // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
                if (thisArg) {
                    T = thisArg;
                }
                // 6. Let A be a new array created as if by the expression new Array(len) where Array is
                // the standard built-in constructor with that name and len is the value of len.
                A = new Array(len);
                // 7. Let k be 0
                k = 0;
                // 8. Repeat, while k < len
                while (k < len) {
                    var kValue, mappedValue;
                    // a. Let Pk be ToString(k).
                    //   This is implicit for LHS operands of the in operator
                    // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
                    //   This step can be combined with c
                    // c. If kPresent is true, then
                    if (k in O) {
                        // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
                        kValue = O[k];
                        // ii. Let mappedValue be the result of calling the Call internal method of callback
                        // with T as the this value and argument list containing kValue, k, and O.
                        mappedValue = callback.call(T, kValue, k, O);
                        // iii. Call the DefineOwnProperty internal method of A with arguments
                        // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
                        // and false.
                        // In browsers that support Object.defineProperty, use the following:
                        // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
                        // For best browser support, use the following:
                        A[k] = mappedValue;
                    }
                    // d. Increase k by 1.
                    k++;
                }
                // 9. return A
                return A;
            };
        }
        // Internet Explorer 8 and older does not support Array.every,
        // so we define it here in that case.
        // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every
        if (!Array.prototype.every) {
            Array.prototype.every = function(fun) {
                "use strict";
                if (this == null) {
                    throw new TypeError();
                }
                var t = Object(this);
                var len = t.length >>> 0;
                if (typeof fun != "function") {
                    throw new TypeError();
                }
                var thisp = arguments[1];
                for (var i = 0; i < len; i++) {
                    if (i in t && !fun.call(thisp, t[i], i, t)) {
                        return false;
                    }
                }
                return true;
            };
        }
        // Internet Explorer 8 and older does not support Array.some,
        // so we define it here in that case.
        // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some
        if (!Array.prototype.some) {
            Array.prototype.some = function(fun) {
                "use strict";
                if (this == null) {
                    throw new TypeError();
                }
                var t = Object(this);
                var len = t.length >>> 0;
                if (typeof fun != "function") {
                    throw new TypeError();
                }
                var thisp = arguments[1];
                for (var i = 0; i < len; i++) {
                    if (i in t && fun.call(thisp, t[i], i, t)) {
                        return true;
                    }
                }
                return false;
            };
        }
        // Define Function.bind if not available
        // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
        if (!Function.prototype.bind) {
            Function.prototype.bind = function(oThis) {
                if (typeof this !== "function") {
                    // closest thing possible to the ECMAScript 5 internal IsCallable function
                    throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
                }
                var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() {
                    return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
                };
                fNOP.prototype = this.prototype;
                fBound.prototype = new fNOP();
                return fBound;
            };
        }
        // initialise the Chain prototype with all functions and constants in math
        for (var prop in math) {
            if (math.hasOwnProperty(prop) && prop) {
                createSelectorProxy(prop, math[prop]);
            }
        }
    })();
});
